====== Konfiguration Server ======

Setup eines Befragungsservers auf Basis eines Debian oder Ubuntu Servers.

  * [[move]]
  * [[sso]]
  * [[shibboleth]]


===== Zusätzliches für www.soscisurvey.de =====

  * OwnCloud
  * Shop/Lizenzserver
  * Softwarelizenzen ''/download''
  * DokuWiki Anleitung
  * Homepage

Firewall: Ausgehend Ports 443 (Lizenzserver) und 25 (SMTP für Mailversand, falls direkt vom Server)

===== Zusätzliches für eigene Server =====

  * Zusätzlicher Benutzer ''s2access'' bekommt ein 32-stelliges Passwort, Login via Passwort möglich
  * Evtl. muss die Platte erst noch den Speicherplatz verwenden:\\ ''lvextend -r -l +100%FREE /dev/ubuntu-vg/ubuntu-lv''

==== Owncloud ====

<code bash>
sudo vim /etc/logrotate.d/owncloud
</code>

<file conf /etc/logrotate.d/owncloud>
/var/www/s2survey/html/owc/data/owncloud.log
{
    weekly
    dateext
    rotate 10
    compress
    delaycompress
    missingok
    notifempty
    su www-data www-data
    create 0640 www-data www-data
    sharedscripts
}
</file>


===== Vorbereitung =====

==== Konfiguration abklären ====

  * Wie erhalte ich die SSH-Zugangsdaten für den Server?
  * Wie lautet/lauten die IP-Adressen des Server?
    * Soll der Server nur via IPv4 oder IPv6 oder via DualStack eingerichtet werden?
  * Wie lautet die (Sub-)Domain, über welche der Befragungsserver erreichbar sein soll?
      * Liegen für diesen bereits ein DNS-Eintrag (A und/oder AAAA) und ein PRT vor?
      * Wer richtet die DNS-Einträge ggf. ein?
  * Ist der Server via Firewall geschützt?
    * Wenn ja, wird der SSH-Zugang (Port 22) für unsere IP-Adressen (95.130.22.98 / 2a02:2940:0:c007::98 und 207.180.212.193) freigegeben oder erhalte ich VPN-Zugang?
    * Wenn ja, dann schalten Sie bitte die Ports 80 (HTTP) und 443 (HTTPS) frei.
    * Wenn nein, dann schränken wir den SSH-Zugriff via ''iptables'' ein. Wie lauten ggf. Ihre Adressbereiche, für welche der SSH-Zugriff erlaubt werden soll?
  * Standardmäßig deaktivieren wir den Nutzer ''root''.
    * Wenn Sie sich weiterhin mit ''root'' und Password (ohne Keyfile) einloggen möchen teilen Sie mir das bitte mit. Dies gilt als unsicher, und wir raten davon ab.
    * Wenn Sie stattdessen einen anderen Benutzer verwenden möchten, legen Sie diesen bitte gleich (mit einem Passwort) an und hinterlegen sie entweder den SSH Public Key selbst (/home/[user]/.ssh/authorised_keys) oder teilen Sie mir diesen mit.
  * Wie erfolgt die Beantragung des SSL-Zertifikats?
  * E-Mail-Adressen
    * Wie lautet die E-Mail-Adresse, an welche Statusmeldungen des Servers und Anfragen von Nutzer:innen verschickt werden sollen?
    * Wir benötigen ein Postfach für E-Mail-Rückläufer inkl. Zugangsdaten, damit der Server die Rückläufer auslesen kann. Für dieses Postfach benötigen wir bitte Benutzername, Kennwort, Name des Mailservers, Ports und verwendete Verschlüsselung
  * Versand von E-Mails
    * Über welchen Relayserver erfolgt der Mailversand (für Serienmails und Benachrichtigungen) und welche Limits sind dort gesetzt?
    * __oder__ ist Port 25 für ausgehende E-Mails ggf. in der Firewall freigegeben?
  * Wie erfolgt die Datensicherung des Servers? Wohin können Daten off-location gesichert werden, falls der Server nicht automatisch durch das Rechenzentrum gesichert wird?
  * Soll nur die Datenbank (erhobene Daten) verschlüsselt werden oder auch die Dateien (relevant, wenn die Befragten z.B. Bilder hochladen)? Im zweiteren Fall müsste das Rechenzentrum eine verschlüsselte Partition/Container einrichten und sicherstellen, dass dieser beim Booten automatisch gemountet wird.


==== Passwörter in KeePass vorbereiten ====

  * SSH-Nutzer
  * SSH Keyfile sosci (via PuttyGen)
  * SSH Keyfile www-ftp (optional)
  * SSH Keyfile für Backup
  * MySQL
    * master
    * s2survey
    * backup
  * Passwort für Backup-Key
  * Monit (admin)
  * Munin (admin)
  * Unix-User: mailer (option, falls lokales Postfach geplant)
  * SoSci-PW: admin
  * SoSci-PW: Cronjobs

 * Zugangsdaten in KeePass notieren
    * IP-Adresse
    * URL
    * Benutername/Passwort
    * MySQL-Port (Weiterleitung)
  * Putty-Eintrag anlegen

<code bash>
sudo apt update
sudo apt dist-upgrade
</code>

==== Benutzer einrichten (soweit erforderlich) ====

<code>
sudo useradd -m sosci
sudo useradd -m s2access
sudo usermod -a -G sudo sosci
sudo usermod -a -G sudo s2access
sudo passwd sosci
sudo passwd s2access
</code>

Noch die Shell ändern:

<code>
sudo vim /etc/passwd
sosci:x:1001:1001::/home/sosci:/bin/bash
s2access:x:1002:1002::/home/s2access:/bin/bash
</code>

Kein Passwort für ''root''

<code>
sudo vim /etc/shadow

root:*:....
</code>
  
Zugang via Keyfile ([[https://vorkbaard.nl/using-putty-and-keyfiles-to-ssh-into-your-ubuntu-12-04-server/|Using PuTTY and keyfiles to SSH...]]), dafür Login als ''sosci''.

<code>
mkdir ~/.ssh
chmod -R 700 ~/.ssh
vim ~/.ssh/authorized_keys
# Inhalt aus ''puttygen''
chmod 600 ~/.ssh/authorized_keys

# Edit /etc/ssh/sshd_config so it contains
sudo vim /etc/ssh/sshd_config

AuthorizedKeysFile %h/.ssh/authorized_keys
PermitRootLogin no

sudo service ssh restart
</code>

Passwort-Login für alles außer s2access deaktivieren, vgl. [[https://serverfault.com/q/285800/100194|How to disable SSH login with password for some users?]]

<code>
sudo vim /etc/ssh/sshd_config
# Am Ende der Datei einfügen

# Allow login only via Keyfile (with exceptions)
PasswordAuthentication no
Match User s2access
PasswordAuthentication yes

sudo service ssh restart
</code>

Login via KeyFile testen (neue Putty-Instanz)

Eventuell noch früheren Nutzer löschen.

<code>
sudo deluser whatever
</code>


==== Fingerprints notieren ====

Key-Fingerprints für SSH in KeePass notieren (''ssh_host_rsa_key'' und alle anderen, mal als md5, mal ohne):

<code bash>
sudo ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key -E md5
ssh -o FingerprintHash=md5 localhost
# oder auch
ssh-keyscan host 2>/dev/null | ssh-keygen -l -f -
ssh -o FingerprintHash=sha256 localhost
# Oder wenn der Server mehrere hat
sudo /usr/sbin/sshd -d
</code>


==== Update ====

Geänderte Konfigurationsdateien ansehen (im Fall eines Updates).

<code>
sudo apt install debsums
sudo debsums -ce
diff /etc/whatever{,.dpkg-dist}
</code>


==== dhparam  ====

Das Erstellen eines Schlüssels für ''dhparam'' dauert eine Weile.

<code bash>
sudo mkdir /var/www/
sudo mkdir /var/www/ssl/
sudo openssl dhparam -out /var/www/ssl/dhparam.pem 4096
</code>

Danach in einer neuen Putty-Instanz weiterarbeiten.


===== SSH Setup =====

SSH KeepAlive, vgl. [[https://patrickmn.com/aside/how-to-keep-alive-ssh-sessions/|How to Keep Alive SSH Sessions]]

<code bash>
sudo vim /etc/ssh/ssh_config

Host *
    ServerAliveInterval 300
    ServerAliveCountMax 2
</code>

<code bash>
sudo vim /etc/ssh/sshd_config

# KeepAlive intervals
ClientAliveInterval 60
ClientAliveCountMax 2
</code>



===== Software =====

  * Einige kleine Dienstprogramme
    * Browser ''elinks'' zum Abruf des Server-Status
  * Einige Sicherheitsfeatures
    * [[http://munin-monitoring.org/wiki/munin.conf|Munin]] zur Überwachung der Server-Leistung -- [[http://www.darkcoding.net/software/setting-up-munin-on-ubuntu/|Setup-Tipps]]
    * Monit zur Überwachung der Erreichbarkeit (inkl. Neustart des Servers bei Problemen)
    * iptables für die Firewall ([[http://www.debian-administration.org/articles/445|Getting IPTables to survive a reboot]])
  * Basis-Software für den Webserver
    * MySQL Datenbank
    * Apache Webserver ([[http://library.linode.com/web-servers/apache/installation/debian-6-squeeze|Apache 2 Web Server on Debian 6]]) oder ''nginx''
    * ''apachetop'' Zur schnellen Kontrolle der Server-Last
    * PHP
    * Certbot zur Aufschaltung von Domains und HTTPS via Let's Encrypt

==== nginx-Paketquellen ====

Quelle: http://nginx.org/en/linux_packages.html

<code bash>
# Install the prerequisites:
sudo apt install curl gnupg2 ca-certificates lsb-release

# Set up the apt repository for stable nginx packages
echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list

# Set up repository pinning to prefer our packages over distribution-provided ones:
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx

# import an official nginx signing key. Fetch the key:
curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key

# Verify that the downloaded file contains the proper key
# The output should contain the full fingerprint 573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62
gpg --dry-run --quiet --import --import-options show-only /tmp/nginx_signing.key

#Finally, move the key to apt trusted key storage (note the "asc" file extension change)
sudo mv /tmp/nginx_signing.key /etc/apt/trusted.gpg.d/nginx_signing.asc
</code>


==== MariaDB Backup ====

Wird bisher eher nicht verwendet, weil die inkrementellen Backups mit den Löschfristen nicht ganz trivial sind. Und die Konfiguration von einem neuen inkrementellen Set pro Monat habe ich noch nicht versucht/geschafft.

<code bash>
curl -LsS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | sudo bash
sudo apt update
sudo apt dist-upgrade
sudo apt install mariadb-backup
</code>

Eventuell zickt ''apt'' herum, weil die Dependencies für MariaDB-Backup nicht funktionieren. In dem Fall ''aptitude'' installieren und damit nochmal die Installation versuchen. Evtl. muss eine neuere Version von MariaDB installiert werden als über die Distribution standardmäßig verfügbar.


==== Installation Pakete ====

<code>
# System aktualisieren
sudo apt update
sudo apt dist-upgrade
sudo apt remove apache2
sudo apt autoremove

# Software installieren
sudo apt install less vim cron elinks debsums fail2ban munin libwww-perl monit unattended-upgrades duplicity duply tmux ncdu
sudo apt install iptables iptables-persistent rkhunter chkrootkit clamav clamav-daemon mailutils apt-transport-https netplan.io
sudo apt install nginx mariadb-server apachetop php-fpm php-mysql php-json php-xml php-gd php-mbstring php-zip php-intl php-imap php-apcu opendkim opendkim-tools apache2-utils
sudo apt install postfix   # Falls der Server Mails nicht via SMTP verschickt

sudo mysql_secure_installation

# Nur bei interner Mailverarbeitung
sudo apt install dovecot-imapd

# Nur für Let's Encrypt !! ALT !!
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx

# Nur für Let's Encrypt (neu)
sudo apt install certbot python3-certbot-nginx

# PHP-FPM macht Probleme, wenn die Version wechselt, das muss manuell erledigt werden
sudo apt-mark hold php-fpm
sudo apt-mark showhold
</code>

**Hinweis:** ''apache2-utils'' sind für htaccess-Passwörter, die für Munin gebraucht werden.

**Hinweis:** Inkrementelle Backups mit ''mariabackup'' ausprobieren.


==== nginx in die www-data-Gruppe aufnehmen ====

<code bash>
sudo usermod -a -G www-data nginx
</code>


===== Sicherheit =====

==== Sicherheitsupdates ====

''unattended-upgrades'' muss noch aktiviert werden, vgl [[https://wiki.debian.org/UnattendedUpgrades|UnattendedUpgrades]]

<code>
sudo dpkg-reconfigure -plow unattended-upgrades
sudo vim /etc/apt/apt.conf.d/50unattended-upgrades
</code>

<file conf  /etc/apt/apt.conf.d/50unattended-upgrades>
Unattended-Upgrade::Mail "info@soscisurvey.de";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "05:00";
</file>


==== Wöchentliche Updates ====

<code>
sudo vim /root/mailinfo.update.sh
</code>

<file bash /root/mailinfo.update.sh>
#!/bin/sh
printf "Subject:[Server] SERVERID Update\n\n%s\n." "`/root/update.sh`" | /usr/sbin/sendmail -t info@soscisurvey.de
</file>

<code>
sudo vim /root/update.sh
</code>

<file bash /root/update.sh>
#!/bin/bash
sudo apt-get update
sudo apt-get --quiet --assume-yes --show-upgraded dist-upgrade
# sudo apt-get autoremove
</file>

<code>
sudo vim /root/kernel-clean.sh
</code>

<file bash /root/kernel-clean.sh>
#!/bin/bash
printf "\n\nAktueller Kernel\n"
uname -a

printf "\n\nZu entfernen\n"
dpkg -l 'linux-[ihs]*' | sed '/^ii/!d;/'"$(uname -r | sed "s/\([-0-9]*\)-\([^0-9]\+\)/\1/")"'/d;s/^[^ ]* [^ ]* \([^ ]*\).*/\1/;/[0-9]/!d' | tee kernel-clean.list

printf "\n"
read -p "Press enter to continue"

cat kernel-clean.list | xargs sudo apt-get -y purge
rm kernel-clean.list
</file>

<code>
sudo vim /root/mailinfo.reboot.sh
</code>

<file bash /root/mailinfo.reboot.sh>
#!/bin/sh
printf "Subject:[Server] SERVERID Reboot\n\nServer rebootet\n." | /usr/sbin/sendmail -t info@soscisurvey.de
</file>

<code>
sudo vim /root/postfix.sh
</code>

<file bash /root/postfix.sh>
#!/bin/bash
# Update the postfix databases and reload
postmap /etc/postfix/virtual
postmap /etc/postfix/senders
postalias /etc/aliases
postfix reload
</file>

<code bash>
sudo chmod u+x /root/kernel-clean.sh
sudo chmod u+x /root/update.sh
sudo chmod u+x /root/mailinfo.update.sh
sudo chmod u+x /root/mailinfo.reboot.sh
sudo chmod u+x /root/postfix.sh
</code>


==== Backup-Scripte (extern) ====

Siehe auch [[https://www.thomas-krenn.com/de/wiki/Backup_unter_Linux_mit_duply|Backup unter Linux mit duply]].

<code bash>
sudo mkdir /root/backup/
sudo mkdir /root/backup/mysql/
sudo mkdir /mnt/backup
sudo touch /mnt/backup/NOT\ MOUNTED
sudo touch /mnt/backup/SERVERID
sudo vim /etc/fstab

# Evtl. benötigt, damit der mount funktioniert
sudo apt install nfs-common
</code>

<file conf /etc/fstab>
nas04.partnergate.com:/volume4/sosci /mnt/backup nfs defaults,nfsvers=3,soft,timeo=30 0 0
</file>

<code bash>
sudo vim /root/backup/backup-daily.sh
</code>

<file bash /root/backup/backup-daily.sh>
#!/bin/bash
echo "



********** Backup (Daily) **********" >> /var/log/backup-daily.log
date >> /var/log/backup-daily.log
mount /mnt/backup >> /var/log/backup-daily.log
echo "
" >> /var/log/backup-daily.log

/usr/bin/duply database backup_purge --force >> /var/log/backup-daily.log
#/usr/bin/duply sosci-database-A backup_purge --force >> /var/log/backup-daily.log
#/usr/bin/duply sosci-database-B backup_purge --force >> /var/log/backup-daily.log
#/usr/bin/duply server backup_purge --force >> /var/log/backup-daily.log
/usr/bin/duply files backup_purge --force >> /var/log/backup-daily.log

sleep 20
umount /mnt/backup >> /var/log/backup-daily.log
</file>

<code bash>
sudo vim /root/backup/backup-weekly.sh
</code>

<file bash /root/backup/backup-weekly.sh>
#!/bin/bash
echo "


***** Backup (Weekly) *****" >> /var/log/backup-weekly.log
date >> /var/log/backup-weekly.log
mount /mnt/backup >> /var/log/backup-weekly.log
echo "
" >> /var/log/backup-weekly.log

/usr/bin/duply server backup_purge --force >> /var/log/backup-weekly.log
# /usr/bin/duply s2server-etc backup_purge --force >> /var/log/backup-weekly.log
# /usr/bin/duply s2server-root backup_purge --force >> /var/log/backup-weekly.log

# Disk space information sent to standard output

echo "***** Backup Weekly Stats *****
"
/usr/local/nagios/libexec/check_disk -w 15% -c 8% -p /mnt/backup
echo ""
du /mnt/backup --max-depth=2 -h

sleep 20

umount /mnt/backup >> /var/log/backup-weekly.log
</file>

<code bash>
sudo chmod 700 /root/backup/backup-daily.sh
sudo chmod 700 /root/backup/backup-weekly.sh
sudo duply server create
sudo duply database create
sudo duply files create
</code>

<code bash>
cd ~
sudo su
gpg --gen-key

gpg --list-secret-keys
gpg --output private.pem --armor --export-secret-key [KEYID]
gpg --output public.pgp --armor --export [KEYID]
chown sosci:sosci private.pem

# Sichern

rm private.pem
rm public.pgp
</code>

**Wichtig:** Schlüssel auf RAM-Disk übertragen und in KeePass sichern ([[https://sourceforge.net/projects/imdisk-toolkit/|ImDisk]]).

Schlüssel-Daten in KeePass notieren und ''private.pem'' herunterladen und in KeePass sichern.

<code bash>
cd ~/.duply/server
mv conf conf.dist
vim conf
vim exclude
</code>

<file bash /root/.duply/server/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_OPTS="--pinentry-mode loopback"
TARGET='file:///mnt/backup/SERVERID/server'
TARGET_USER=''
TARGET_PASS=''
SOURCE='/'
MAX_AGE=3M
MAX_FULLBKP_AGE=2M
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/server/exclude>
/initrd.img
/lib64
/vmlinuz
/dev/
/proc/
/sys/
/tmp/
/run/
/mnt/
/media/
/lost+found/

/home/sosci/backup/
/usr/share/doc/
/usr/src/
/var/backup/
/var/cache/
/var/www/
/var/tmp/
/var/lib/mysql/
/var/lib/lxcfs/cgroup/
/root/.cache/
/root/backup/mysql/
</file>

<code bash>
cd ~/.duply/database
mv conf conf.dist
vim conf
vim pre
vim post
chmod u+x pre
chmod u+x post
</code>

<file bash /root/.duply/database/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_OPTS="--pinentry-mode loopback"
TARGET='file:///mnt/backup/SERVERID/database'
SOURCE='/root/backup/mysql'
MAX_AGE=1M
MAX_FULL_BACKUPS=31
MAX_FULLBKP_AGE=23h
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/database/pre>
#!/bin/sh
datetime=`date +%Y-%m-%d_%H-%M-%S`
mysqldump -ubackup -p[PASSWORD_BACKUP] --databases s2survey --ignore-table sosci.sosci_receivers --ignore-table sosci.sosci_contacts --ignore-table sosci.sosci_users --quick --single-transaction | gzip -9 > /root/backup/mysql/sosci-survey-$datetime.sql.gz
mysqldump -ubackup -p[PASSWORD_BACKUP] --quick --single-transaction s2survey sosci_receivers | gzip -9 > /root/backup/mysql/sosci-receivers-$datetime.sql.gz
mysqldump -ubackup -p[PASSWORD_BACKUP] --quick --single-transaction s2survey sosci_contacts | gzip -9 > /root/backup/mysql/sosci-contacts-$datetime.sql.gz
mysqldump -ubackup -p[PASSWORD_BACKUP] --quick --single-transaction s2survey sosci_users | gzip -9 > /root/backup/mysql/sosci-users-$datetime.sql.gz

</file>

Auf ''www.soscisurvey.de'' auch noch....

<file bash /root/.duply/database/pre>
mysqldump -ubackup -p[PASSWORD_BACKUP] --databases rtr_mobile --quick --single-transaction | gzip -9 > /root/backup/mysql/rtr-$datetime.sql.gz
mysqldump -ubackup -p[PASSWORD_BACKUP] --databases distribution --quick --single-transaction | gzip -9 > /root/backup/mysql/customers-$datetime.sql.gz
</file>

<file bash /root/.duply/database/post>
#!/bin/sh
rm /root/backup/mysql/*.sql.gz
</file>

<code bash>
cd ~/.duply/files
mv conf conf.dist
vim conf
vim exclude
</code>

Das Backup kann entweder auf das lokale Laufwerk (gemountetet Backup-Speicher) oder vis SFTP erfolgen. Eventuell ist dafür noch das Modul ''python-paramiko'' (via ''apt'')) erforderlich.

<file bash /root/.duply/files/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_OPTS="--pinentry-mode loopback"
TARGET='file:///mnt/backup/SERVERID/files'
SOURCE='/var/www/s2survey/html'
MAX_AGE=1M
MAX_FULL_BACKUPS=2
MAX_FULLBKP_AGE=14D
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/files/conf>
GPG_KEY='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
GPG_PW=''
TARGET='sftp://s2backup@207.180.212.193/sosci/files'
TARGET_USER='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
TARGET_PASS='XXXXXXXXXXXXXXXXXXXXXXXXXXX'
SOURCE='/var/www/soscisurvey/html'
MAX_AGE=13D
MAX_FULL_BACKUPS=1
MAX_FULLBKP_AGE=1M
DUPL_PARAMS="$DUPL_PARAMS --full-if-older-than $MAX_FULLBKP_AGE "
</file>

<file bash /root/.duply/files/exclude>
/var/www/s2survey/html/view/
/var/www/s2survey/html/system/cache/
/var/www/s2survey/html/system/temp/
/var/www/s2survey/html/system/session/
/var/www/s2survey/html/system/lock/

/var/www/s2survey/html/files/protected/000000/
/var/www/s2survey/html/CUSTOM_PROJECT/
</file>

Zum Testen der Backups muss man ''gpg'' verraten, woher das Passwort zum Schlüssel kommt:

<code bash>
export GPG_TTY=$(tty)
</code>


==== Backup (interne Sicherung) ====

<code bash>
sudo mkdir /root/backup/
sudo mkdir /root/backup/mysql/
sudo vim /root/backup/backup-daily.sh
</code>

<file bash /root/backup/backup-daily.sh>
#!/bin/sh
find /root/backup/mysql -name "*.gz" -type f -mtime +30 -exec rm -f {} \;

datetime=`date +%Y-%m-%d_%H-%M-%S`
mysqldump -ubackup -pXXXXXXXXXXXXXXXXXXXX --databases s2survey --ignore-table s2survey.s2_receivers --quick --single-transaction | gzip -9 > /root/backup/mysql/s2survey-$datetime.sql.gz
mysqldump -ubackup -pXXXXXXXXXXXXXXXXXXXX --quick --single-transaction s2survey s2_receivers | gzip -9 > /root/backup/mysql/s2survey-receivers-$datetime.sql.gz
</file>


==== NTP-Server einstellen ====

s. [[https://help.ubuntu.com/lts/serverguide/NTP.html|Time Synchronization]]

<code>
sudo vim /etc/systemd/timesyncd.conf
NTP=ptbtime1.ptb.de
FallbackNTP=ntp1.lrz.de
</code>

==== fail2ban ====

Den anderen Befragungsserver als vertrauenswürdig akzeptieren.

<code bash>
sudo vim /etc/fail2ban/jail.local
</code>

<file conf /etc/fail2ban/jail.local>
[DEFAULT]
ignoreip = 127.0.0.1/8 95.130.22.98
findtime = 600
bantime  = 600
maxretry = 3
destemail = info@soscisurvey.de
chain = INPUT

[serial-fail]
# SoSci Surveys locks IPs on its own, this is only the upper limit
enabled  = true
port     = http,https
filter   = serial-fail
action   = iptables-allports
           mail-whois[name=Serial Fail, dest=info@soscisurvey.de]
logpath  = /var/www/s2survey/html/system/logfiles/token-fail.log
maxretry = 50
bantime  = 7200

[nginx-forbidden]
# Requests to PHP files that must not be accessed
enabled  = true
filter   = nginx-forbidden
# action   = iptables-allports
action = sendmail-whois-ipmatches[name=Serial Fail, dest=info@soscisurvey.de]
logpath  = /var/www/s2survey/log/n-forbidden.log
maxretry = 2
bantime  = 30


[sshd]
enabled = true
mode    = aggressive

[php-url-fopen]
enabled = true

# Nur wenn installiert
[owncloud]
enabled = true
</file>

<code bash>
sudo vim /etc/fail2ban/filter.d/serial-fail.conf
</code>

<file conf /etc/fail2ban/filter.d/serial-fail.conf>
[Definition]
failregex = <HOST>\s+(invalid|serial|case|token|deliveryToken|login\.preserve)
ignoreregex =
</file>

<code bash>
sudo vim /etc/fail2ban/filter.d/nginx-forbidden.conf
</code>

<file conf /etc/fail2ban/filter.d/nginx-forbidden.conf>
# Fail2Ban filter to match web requests for "access denied" URLs
#

[INCLUDES]

# Load regexes for filtering
# before = botsearch-common.conf

[Definition]

failregex = ^<HOST> \- \- .*? \"(GET|POST|HEAD) .*?\" 403 .+$

ignoreregex =

datepattern = {^LN-BEG}%%ExY(?P<_sep>[-/.])%%m(?P=_sep)%%d[T ]%%H:%%M:%%S(?:[.,]%%f)?(?:\s*%%z)?
              ^[^\[]*\[({DATE})
              {^LN-BEG}
</file>

<code bash>
sudo vim /etc/fail2ban/filter.d/owncloud.conf
</code>

<file>
[Definition]
failregex={"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)","level":2,"time":".*"}

ignoreregex =
</file>

<code bash>
sudo vim /etc/fail2ban/action.d/sendmail-common.conf
</code>

Hier noch die Inhalte hinter ''actionstart'' und ''actionstop'' entfernen (minus 48 Zeilen), also

<code>
[INCLUDES]
after = sendmail-common.local

[Definition]
actionstart =
actionstop =
actioncheck =
actionban =
actionunban =

[Init]
mailcmd = /usr/sbin/sendmail -f "<sender>" "<dest>"
dest = root
sender = fail2ban
sendername = Fail2Ban
</code>

Und auch in den anderen Dateien ... wobei da evtl. ''actionban'' stehen bleibt. Muss aber nicht sein.

<code bash>
sudo vim /etc/fail2ban/action.d/mail.conf
</code>

<file conf /etc/fail2ban/action.d/mail.conf>
[Definition]
norestored = 1
actionstart =
actionstop =
actioncheck =

actionban = printf %%b "Hi,\n
            The IP <ip> has just been banned by Fail2Ban after
            <failures> attempts against <name>.\n
            Regards,\n
            Fail2Ban"|mail -s "[Fail2Ban] <name>: banned <ip> from <fq-hostname>" <dest>
            
actionunban =

[Init]
name = default
dest = root
</file>


<code bash>
sudo systemctl reload fail2ban
</code>
==== rkhunter ====

''xinet '' wird für Nagios erlaubt.

<code>
sudo vim /etc/rkhunter.conf

PKGMGR=DPKG
XINETD_ALLOWED_SVC=/etc/xinetd.d/nrpe

sudo rkhunter --propupd

sudo vim /etc/default/rkhunter
CRON_DAILY_RUN="true"
CRON_DB_UPDATE="true"
REPORT_EMAIL="info@soscisurvey.de"
APT_AUTOGEN="true"
</code>

<code bash>
mkdir /var/log/chkrootkit/
sudo vim /etc/chkrootkit.conf
sudo vim /etc/chkrootkit/chkrootkit.conf
RUN_DAILY_OPTS="-q"
RUN_DAILY="true"
</code>

Anschließend einmal laufen lassen, um eine Vergleichsdatei zu bekommen:

<code bash>
sudo /etc/cron.daily/chkrootkit
</code>

Und dann die Ausgabe prüfen und den Anweisungen folgen:

<code>
To create this file containing all output from today's run, do (as root)
# cp -a /var/log/chkrootkit/log.today /var/log/chkrootkit/log.expected
# (note that unedited output is in /var/log/chkrootkit/log.today.raw)
</code>

<code bash>
sudo less /var/log/chkrootkit/log.today
sudo cp -a /var/log/chkrootkit/log.today /var/log/chkrootkit/log.expected
</code>


===== Einstellungen =====

    * Tägliches Backup (MySQL und Dateien) via Cronjob
      * [[http://www.howtoforge.de/anleitung/verschlusselte-ftp-backups-mit-duplicity-und-duply-erstellen-debian-squeeze/|Verschlüsselte FTP Backups mit duplicity und duply erstellen]]
    *  Abruf des Server-Status ermöglichen [[http://httpd.apache.org/docs/2.2/mod/mod_status.html|Apache Module mod_status]]



===== Firewall =====

Als Firewall kommt ''iptables'' zum Einsatz, damit nicht versehentlich irgendwelche Ports nach außer offenstehen. Außerdem lassen sich so einige Angriffe abblocken.

<code>
sudo systemctl enable netfilter-persistent
sudo mkdir /etc/iptables
# sudo bash -c "iptables-save > /etc/iptables/rules.v4"
# sudo bash -c "ip6tables-save > /etc/iptables/rules.v6"
# Gleiches Regelset für IPv4 un IPv6
cd /etc/iptables
sudo vim rules.base
sudo ln -s rules.base rules.v4
sudo ln -s rules.base rules.v6
</code>

  * Die Regeln werden in ''/etc/iptables/rules.base'' gespeichert, die Dateien ''rules.v4'' und ''rules.v6'' verweisen nur auf diese Datei, um eine Doppel- und Fehlkonfiguration zu vermeiden.
  * Zum Ausprobieren (IPv4 und IPv4 getrennt!) sehr gut geeignet: [[http://www.ipv6scanner.com/]] oder [[https://www.ipvoid.com/port-scan/]]

<file conf rules.base>
## Custom rules
## Based on https://gist.github.com/jirutka/3742890
*filter
# 
# Base policy
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# 
# Drop non-conforming packets, such as malformed headers, etc.
-A INPUT -m conntrack --ctstate INVALID -j DROP
#
# Antispoofing
# Block remote packets claiming to be from a loopback address.
-4 -A INPUT -s 127.0.0.0/8 ! -i lo -j DROP
-6 -A INPUT -s ::1/128 ! -i lo -j DROP
-6 -A INPUT -i $WAN_IF -s FC00::/7 -j DROP
-6 -A FORWARD -s ::1/128 -j DROP
-6 -A FORWARD -i $WAN_IF -s FC00::/7 -j DROP
#
# Don't attempt to firewall internal traffic on the loopback device.   
-A INPUT -i lo -j ACCEPT
-A INPUT -s localhost -j ACCEPT
#
# Whitelist Monitoring Server
-4 -A INPUT -s 95.130.16.86 -j ACCEPT
-4 -A INPUT -s 95.130.16.67 -j ACCEPT
#
# Continue connections that are already established or related to an established
# connection.
-4 -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-6 -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
#
# Drop all packets that are going to broadcast, multicast or anycast address.
-4 -A INPUT -m addrtype --dst-type BROADCAST -j DROP
-4 -A INPUT -m addrtype --dst-type MULTICAST -j DROP
-4 -A INPUT -m addrtype --dst-type ANYCAST -j DROP
-4 -A INPUT -d 224.0.0.0/4 -j DROP
#
# Chain for preventing ping flooding - up to 6 pings per second from a single
# source, again with log limiting. Also prevents us from ICMP REPLY flooding
# some victim when replying to ICMP ECHO from a spoofed source.
-N ICMPFLOOD
-A ICMPFLOOD -m recent --set --name ICMP --rsource
-A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -m limit --limit 1/sec --limit-burst 1 -j LOG --log-prefix "iptables[ICMP-flood]: "
-A ICMPFLOOD -m recent --update --seconds 1 --hitcount 6 --name ICMP --rsource --rttl -j DROP
-A ICMPFLOOD -j ACCEPT
#
# Chain for preventing SSH brute-force attacks.
# Permits 10 new connections within 5 minutes from a single host then drops
# incomming connections from that host. Beyond a burst of 100 connections we
# log at up 1 attempt per second to prevent filling of logs.
-N SSHBRUTE
-A SSHBRUTE -m recent --name SSH --set
-A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[SSH-brute]: "
-A SSHBRUTE -m recent --name SSH --update --seconds 300 --hitcount 10 -j DROP
#
# Allow SSH from certain IPs
# Allow from www.onlineforschung.org
-4 -A SSHBRUTE -s 207.180.212.193 -j ACCEPT
# Allow from s2survey.net
-4 -A SSHBRUTE -s 95.130.22.100 -j ACCEPT
# Allow from MWN/LMU
-4 -A SSHBRUTE -s 138.246.2.0/24 -j ACCEPT
-4 -A SSHBRUTE -s 141.84.32.0/24 -j ACCEPT
-4 -A SSHBRUTE -s 141.84.33.0/24 -j ACCEPT
-4 -A SSHBRUTE -s 141.84.33.32 -j ACCEPT
# Allow from LMU/VPN LRZ_VPN_public_LMU_IPv6
-6 -A SSHBRUTE -s 2001:4ca0:4fff:2::/48 -j ACCEPT
# Optionally allow from m-net
-4 -A SSHBRUTE -s 62.216.206.12/24 -j ACCEPT
-4 -A SSHBRUTE -s 62.216.202.189/24 -j ACCEPT
-6 -A SSHBRUTE -s 2001:a61:2bac:8201::/48 -j ACCEPT
-6 -A SSHBRUTE -s 2001:a61:2b86:c201::/48 -j ACCEPT
-A SSHBRUTE -j DROP
#
# NUL Packages (Portscanner) blockieren
-A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# SYN flood blockieren
-A INPUT -p tcp ! --syn -m state --state NEW -j DROP
# XMAS blockieren
-A INPUT -p tcp --tcp-flags ALL ALL -j DROP
#
#
## SMTP only if receiving emails
# -A INPUT -p tcp -m tcp --dport 25 -j ACCEPT
#
## HTTP/S
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
#
## Monit (port 2812)
-A INPUT -p tcp -m tcp --dport 2812 -m state --state NEW -m limit --limit 5/min --limit-burst 20 -j ACCEPT
#
#
# Test Ports
#-A INPUT -p tcp -m tcp --dport 8080 -j ACCEPT
#-A INPUT -p tcp -m tcp --dport 8081 -j ACCEPT									   
#
# Ping (outgoing)
-A OUTPUT -p icmp -j ACCEPT
-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
#
###############################################################################
# 3. GENERAL RULES                                                            #
#                                                                             #
# This section contains general rules that should be suitable for most hosts. #
###############################################################################
#
# Accept worldwide access to SSH and use SSHBRUTE chain for preventing and filter IPs
-4 -A INPUT -p tcp --dport 22 --syn -m state --state NEW -j SSHBRUTE
-6 -A INPUT -p tcp --dport 22 --syn -m conntrack --ctstate NEW -j SSHBRUTE
#
# Permit useful IMCP packet types for IPv4
# Note: RFC 792 states that all hosts MUST respond to ICMP ECHO requests.
# Blocking these can make diagnosing of even simple faults much more tricky.
# Real security lies in locking down and hardening all services, not by hiding.
-4 -A INPUT -p icmp --icmp-type 0  -m conntrack --ctstate NEW -j ACCEPT
-4 -A INPUT -p icmp --icmp-type 3  -m conntrack --ctstate NEW -j ACCEPT
-4 -A INPUT -p icmp --icmp-type 11 -m conntrack --ctstate NEW -j ACCEPT
#
# Permit needed ICMP packet types for IPv6 per RFC 4890.
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 1   -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 2   -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 3   -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 4   -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 133 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 134 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 135 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 136 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 137 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 141 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 142 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 130 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 131 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 132 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 143 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 148 -j ACCEPT
-6 -A INPUT              -p ipv6-icmp --icmpv6-type 149 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 151 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 152 -j ACCEPT
-6 -A INPUT -s fe80::/10 -p ipv6-icmp --icmpv6-type 153 -j ACCEPT
#
# Permit IMCP echo requests (ping) and use ICMPFLOOD chain for preventing ping
# flooding.
-4 -A INPUT -p icmp --icmp-type 8  -m conntrack --ctstate NEW -j ICMPFLOOD
-6 -A INPUT -p ipv6-icmp --icmpv6-type 128 -j ICMPFLOOD
#								   
#
# Do not log packets that are going to ports used by SMB
# (Samba / Windows Sharing).
-A INPUT -p udp -m multiport --dports 135,445 -j DROP
-A INPUT -p udp --dport 137:139 -j DROP
-A INPUT -p udp --sport 137 --dport 1024:65535 -j DROP
-A INPUT -p tcp -m multiport --dports 135,139,445 -j DROP
#
# Do not log packets that are going to port used by UPnP protocol.
-A INPUT -p udp --dport 1900 -j DROP
#
# Do not log late replies from nameservers.
-A INPUT -p udp --sport 53 -j DROP
#
# Good practise is to explicately reject AUTH traffic so that it fails fast.
-A INPUT -p tcp --dport 113 --syn -m conntrack --ctstate NEW -j REJECT --reject-with tcp-reset
#
# Prevent DOS by filling log files.
# Note, that more likely the limits from nginx will trigger
-A INPUT -m limit --limit 1/second --limit-burst 100 -j LOG --log-prefix "iptables[DOS]: "
#
# Final action
-A INPUT -j DROP
#
COMMIT
</file>

  * Der Port 25 (incoming) ist nur auf ''mail'' freigegeben.

Die Konfiguration beim Neustart laden (ab Ubuntu 18), vgl. [[https://nucco.org/2018/05/ubuntu-18-04-chronicles-applying-firewall-rules-on-startup-pre-network.html|sudo systemctl enable iptables-load.service]]

<code bash>
sudo vim /etc/iptables/restore.sh
sudo vim /etc/systemd/system/iptables-load.service
sudo chmod u+x /etc/iptables/restore.sh
</code>

<file bash /etc/iptables/restore.sh>
#!/bin/bash
/sbin/iptables-restore < /etc/iptables/rules.v4
/sbin/ip6tables-restore < /etc/iptables/rules.v6
</file>

<file bash /etc/systemd/system/iptables-load.service>
[Unit]
Description = Load firewall rules on startup

[Service]
Type=oneshot
ExecStart=/etc/iptables/restore.sh

[Install]
WantedBy=network-pre.target
</file>

Den Service aktivieren und zum Testen gleich noch starten (''--now'').

<code bash>
sudo systemctl enable iptables-load.service --now
</code>


==== Schutz gegen DOS-Angriffe (optional) ====

  * [[https://javapipe.com/blog/iptables-ddos-protection/|Verbindungen limitieren gegen DoS-Angriffe]]


===== SFTP ======

FTP-Benutzer einrichten, der nur auf SoSci Survey zugreifen kann.

<code bash>
sudo groupadd www-ftp
sudo useradd -g www-ftp -m -d /home/www-ftp -s /sbin/nologin www-ftp
sudo mkdir /home/www-ftp/.ssh
sudo vim /home/www-ftp/.ssh/authorized_keys
# Schlüssel aus PuttyGen einsetzen
sudo chown -R www-ftp:www-ftp /home/www-ftp/.ssh
sudo chmod 700 /home/www-ftp/.ssh/
sudo chmod 600 /home/www-ftp/.ssh/authorized_keys
</code>

Verzeichnisse für SoSci Survey anlegen

<code bash>
sudo mkdir /var/www/s2survey
sudo mkdir /var/www/s2survey/html
sudo mkdir /var/www/s2survey/log/
sudo chown root:root /var/www/s2survey
sudo chown -R www-data:www-data /var/www/s2survey/log
sudo chown -R www-ftp:www-data /var/www/s2survey/html
sudo chmod 770 /var/www/s2survey/html
</code>

<code bash>
sudo vim /etc/passwd
</code>

<file conf /etc/passwd>
www-ftp:x:1003:1003::/html:/usr/sbin/nologin
</file>

<code bash>
sudo vim /etc/ssh/sshd_config
</code>

<file conf /etc/ssh/sshd_config>
#Subsystem       sftp    /usr/libexec/openssh/sftp-server
Subsystem       sftp    internal-sftp

# am Ende (!) der Datei noch
Match User www-ftp
    AuthorizedKeysFile /home/www-ftp/.ssh/authorized_keys
    ChrootDirectory /var/www/s2survey
    ForceCommand internal-sftp
    AllowTcpForwarding no
    X11Forwarding no

Match all
</file>

<code bash>
sudo systemctl reload sshd
</code>

Wenn es Probleme gibt, ''sshd'' im Debug-Mode laufen lassen:

<code bash>
sudo /usr/sbin/sshd -d -p 3321
</code>

===== Webserver =====

==== PHP-FPM ====

PHP 8.1 sollte es schon sein.

  * https://www.php.net/supported-versions.php
  * https://www.linuxcapable.com/how-to-install-php-8-1-on-debian-11-bullseye/

Der Standard-Pool ''www.conf'' muss gelöscht oder umbenennt werden, z.B. ''mv www.conf www.conf.dpkg-dist''.

Die Einstellung ''pm.max_children = 32'' ist für 6 GB RAM und Erhebungen mit starkem Ansturm ausgelegt.

!FIXME In der aktuellen Version verwendet ''nginx'' Benutzer:Gruppe ''nginx:nginx''. Das muss in der PHP-Konfiguration und (!) bei den HTML-Dateizugriffsrechten entsprechend vorgesehen werden.

<code bash>
sudo mv /etc/php/7.4/fpm/pool.d/www.conf /etc/php/7.4/fpm/pool.d/www.conf.dist
sudo vim /etc/php/7.4/fpm/pool.d/primary.conf
sudo vim /etc/php/7.4/fpm/pool.d/secondary.conf

sudo mv /etc/php/8.1/fpm/pool.d/www.conf /etc/php/8.1/fpm/pool.d/www.conf.dist
sudo vim /etc/php/8.1/fpm/pool.d/primary.conf
sudo vim /etc/php/8.1/fpm/pool.d/secondary.conf
</code>

<file conf /etc/php/7.4/fpm/pool.d/primary.conf>
[primary]
user = www-data
group = www-data
listen = /run/php/php-fpm.sock
listen.backlog = 1023
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 64
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 2500
pm.status_path = /status.primary
ping.path = /ping.primary
env[PATH] = /usr/local/bin:/usr/bin:/bin

; evtl. für SimpleSAML
env[SIMPLESAMLPHP_CONFIG_DIR] = /var/www/shibboleth/config
</file>

<file conf /etc/php/7.4/fpm/pool.d/secondary.conf>
[secondary]
user = www-data
group = www-data
listen = 127.0.0.1:9002
listen.backlog = 511
listen.owner = www-data
listen.group = www-data
listen.allowed_clients = 127.0.0.1
pm = ondemand
pm.max_children = 10
pm.process_idle_timeout = 10s;
pm.max_requests = 500
pm.status_path = /status.secondary
ping.path = /ping.secondary
env[PATH] = /usr/local/bin:/usr/bin:/bin
</file>

Und dann noch ein paar Änderungen in der php.ini (ehem. ''/etc/php/7.4/fpm/conf.d/10-sosci.ini'')

<file conf /etc/php/8.1/fpm/conf.d/10-sosci.ini>
display_errors = On
display_startup_errors = On
post_max_size = 256M
upload_max_filesize = 256M
max_file_uploads = 50
mail.add_x_header = Off
</file>


===== nginx =====

Zertifikate und Schlüssel nach ''/var/www/ssl/'' kopieren.

<code bash>
sudo vim /etc/nginx/nginx.conf

# Entfernen
ssl_prefer_server_ciphers
ssl_protocols
</code>

Die eigentliche Konfiguration wird dann in ''/etc/nginx/conf.d'' untergebracht.

<file conf /etc/nginx/conf.d/ssl.conf>
# SSL Settings

ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE, and TLSv1
ssl_prefer_server_ciphers on;
ssl_ciphers EECDH+AESGCM:EDH+AESGCM;
ssl_ecdh_curve secp384r1;
ssl_dhparam /var/www/ssl/dhparam.pem;
## Use a SSL/TLS cache for SSL session resume.
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_stapling on;
ssl_stapling_verify on;
# HTTP-Strict Transport Security (HSTS)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";

# Information disclosure
server_tokens off;
</file>

<file conf /etc/nginx/conf.d/logs.conf>
# Logging Settings

log_format noip '0.0.0.0 - $remote_user [$time_local]  '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "$http_user_agent"';

log_format noagent '0.0.0.0 - $remote_user [$time_local]  '
    '"$request" $status $body_bytes_sent '
    '"$http_referer" "na"';
</file>

<file conf /etc/nginx/conf.d/gzip.conf>
gzip_disable "msie6";
gzip_vary on;
</file>

<file conf /etc/nginx/fastcgi_params>
fastcgi_connect_timeout 65;
fastcgi_send_timeout    180;
fastcgi_read_timeout    900;
</file>

<file conf /etc/logrotate.d/nginx>
/var/log/nginx/*.log
/var/www/s2survey/log/*.log {
        weekly
        missingok
        rotate 28
        dateext
        compress
        delaycompress
        notifempty
        create 0640 www-data adm
        sharedscripts
        prerotate
                if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
                        run-parts /etc/logrotate.d/httpd-prerotate; \
                fi \
        endscript
        postrotate
                # [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
                invoke-rc.d nginx rotate >/dev/null 2>&1
        endscript
}
</file>

<file conf /etc/nginx/mime.types>
+
application/wasm                      wasm;
</file>

<code bash>
systemctl stop php8.1-fpm
systemctl start php8.1-fpm
</code>


==== MySQL ====

<code bash>
sudo vim /etc/mysql/conf.d/s2survey.cnf
sudo systemctl restart mysql
</code>

<file conf /etc/mysql/conf.d/s2survey.cnf>
[mysqld]
key_buffer_size         = 64M
max_allowed_packet      = 32M
max_connections         = 512

slow_query_log          = 1
slow_query_log_file     = /var/log/mysql/mysql-slow.log
long_query_time         = 5

# InnoDb configuration
innodb_file_per_table = 1
innodb_buffer_pool_size = 2G # ~50% of System Memory
innodb_log_file_size=513M # ~50% of innodb_buffer_pool_size 
innodb_log_buffer_size = 512M
# innodb_thread_concurrency = 8
innodb_lock_wait_timeout=20
innodb_io_capacity=1000 # Match system's capacity, see iostat in the sysstat package
# innodb_additional_mem_pool_size = 20M
# innodb_data_file_path = ibdata1:50M:autoextend

# pid-file      = /var/run/mysqld/mysqld.pid

skip-log-bin
# binlog_expire_log_seconds = 172800

[mariadb]
log_warnings=1
</file>

<code bash>
sudo systemctl stop mysql
sudo systemctl start mysql
sudo su
mysql
</code>

=== Datenverzeichnis MySQL ===

Eventuell muss das [[https://www.digitalocean.com/community/tutorials/how-to-move-a-mysql-data-directory-to-a-new-location-on-ubuntu-16-04|Datenverzeichnis von MySQL]] noch verschoben werden.

<code bash>
sudo systemctl stop mysql
sudo mv /var/lib/mysql /ENCRYPTED/mysql
cd /etc/mysql
grep -R datadir *
sudo vim /etc/mysql/mariadb.conf.d/50-server.cnf
sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf

datadir=/ENCRYPTED/mysql

sudo systemctl start mysql
</code>

=== Verschlüsselung ===

dann verwenden wir noch die [[https://mariadb.com/kb/en/data-at-rest-encryption-overview/|Data-at-Rest-Verschlüsselung von MariaDB]].

<code bash>
sudo mkdir /etc/mysql/encryption
sudo touch /etc/mysql/encryption/keyfile
sudo chown mysql:mysql /etc/mysql/encryption/keyfile
sudo chmod 600 /etc/mysql/encryption/keyfile
sudo sh -c 'echo -n "1;" >> /etc/mysql/encryption/keyfile'
sudo sh -c 'openssl rand -hex 32 >> /etc/mysql/encryption/keyfile'

sudo less /etc/mysql/encryption/keyfile

sudo touch /etc/mysql/conf.d/encrypt.cnf
sudo chown mysql:mysql /etc/mysql/conf.d/encrypt.cnf
sudo chmod 600 /etc/mysql/conf.d/encrypt.cnf

sudo vim /etc/mysql/conf.d/encrypt.cnf
</code>

<code conf>
[mariadb]
plugin_load_add = file_key_management
loose_file_key_management_filename = /etc/mysql/encryption/keyfile
loose_file_key_management_encryption_algorithm = AES_CTR

# InnoDB/XtraDB Encryption
innodb_encrypt_tables = ON
innodb_encrypt_temporary_tables = ON
innodb_encrypt_log = ON
innodb_encryption_threads = 4
innodb_encryption_rotate_key_age = 1

# InnoDb configuration
innodb_file_per_table = 1
innodb_buffer_pool_size = 2G
innodb_log_file_size=128M
innodb_log_buffer_size = 512M
innodb_thread_concurrency = 8
innodb_lock_wait_timeout=20

[mariabackup]
user=mariabackup
password=###PWD###
</code>

Den Inhalt des Keyfiles nach KeePass sichern.

<code bash>
sudo systemctl restart mariadb
</code>

Nach dem Anlegen einer Tabelle kann man dann in der Tabelle ''`information_schema`.`INNODB_TABLESPACES_ENCRYPTION`'' nachsehen, ob die Verschlüsselung aktiv ist. Dort müssen ''ENCRYPTION_SCHEME'' und ''CURRENT_KEY_ID'' den Wert 1 haben.

<code>
sudo su
mysql

USE information_schema;
SELECT * FROM INNODB_TABLESPACES_ENCRYPTION;
</code>


=== MySQL-Nutzer ===

Einen neuen Root-Nutzer mit Passwort anlegen, vgl [[https://tableplus.io/blog/2018/10/how-to-create-a-superuser-in-mysql.html|https://tableplus.io/blog/2018/10/how-to-create-a-superuser-in-mysql.html]]

<code mysql>
CREATE USER 'master'@'localhost' IDENTIFIED BY 'XXXXXXXXXXXXXX';
GRANT ALL PRIVILEGES ON *.* TO 'master'@'localhost' WITH GRANT OPTION;
SHOW GRANTS FOR master@localhost;
FLUSH PRIVILEGES;

CREATE DATABASE s2survey;
CREATE USER 's2survey'@'localhost' IDENTIFIED BY 'XXXXXXXXXXXXXX';
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'XXXXXXXXXXXXXX';
GRANT ALL PRIVILEGES ON s2survey.* TO 's2survey'@'localhost';
GRANT USAGE ON *.* TO 'backup'@'localhost';
GRANT SELECT, LOCK TABLES ON `mysql`.* TO 'backup'@'localhost';
GRANT SELECT, LOCK TABLES, SHOW VIEW, EVENT, TRIGGER ON `myschema`.* TO 'backup'@'localhost';
FLUSH PRIVILEGES;

QUIT;
</code>



==== Websites ====

Die **alte Lösung** lief über ''sites-available'' -> ''sites-enabled''.

<code bash>
sudo vim /etc/nginx/nginx.conf

# im http-Block ergänzen
include /etc/nginx/sites-enabled/*;

sudo mkdir /etc/nginx/sites-available
sudo mkdir /etc/nginx/sites-enabled
</code>

Die **neue Lösung** ist, dass die ''.conf''-Dateien einfach in ''./conf.d'' angelegt werden.


**Notiz:** Eventuell müssen für Nicht-SoSci-Inhalte noch ein paar Header gesetzt werden, vgl. [[https://securityheaders.com/?q=www.soscisurvey.de&followRedirects=on|securityheaders.com]]

Erstmal ein Self-Signed Certificate erstellen

<code bash>
sudo su
cd /var/www/ssl
openssl req -x509 -nodes -newkey rsa:2048 -keyout selfsigned.key -out selfsigned.crt -days 365
</code>

Auf ''s2survey.net'' werden Datei-Uploads bis zu 256 MB erlaubt, also ''post_max_size = 256M''.

<file conf /etc/nginx/sites-available/01.s2survey.conf>
# PHP-FPM processes to be used by SoSci Survey
upstream sosci {
    server unix:/run/php/php-fpm.sock max_fails=5 fail_timeout=30s;
    server 127.0.0.1:9002 backup;
}

# Limitation for requets per second
# Expect no more than 25 PHP requests (not images) per second
# This is 1500 requests per minute = 30 people in a classroom doing 50 suggest-question requests per minute
# May use a smaller limit of 5r/s for www.soscisurvey.de
limit_req_zone $binary_remote_addr zone=php:10m rate=25r/s;
# (Much) less login attempts - for the SoSci Panel at the moment
limit_req_zone $binary_remote_addr zone=login:10m rate=15r/m;

# Main domain(s)
server {
        listen [::]:443 default ipv6only=on;
        listen 443 ssl http2;

        server_name s2survey.net s2survey.de;

        # The server certificate must appear before the chained certificates in the combined file
        # cat www.example.com.crt bundle.crt > www.example.com.chained.crt
        # ssl_certificate_key /etc/letsencrypt/live/s2survey.net/privkey.pem; # fallback
        # ssl_certificate /etc/letsencrypt/live/s2survey.net/fullchain.pem; # fallback
        ssl_certificate_key /var/www/ssl/selfsigned.key;
        ssl_certificate /var/www/ssl/selfsigned.crt;
        # ssl_certificate_key /var/www/ssl/s2survey.2017.key;
        # ssl_certificate /var/www/ssl/s2survey.2018.cert;

        # Content
        root /var/www/s2survey/html;
        index index.php index.html index.htm;
        error_page 404 = /spellcheck.php;
        client_max_body_size    80M;

        access_log /var/www/s2survey/log/n-access.log noagent;
        error_log /var/www/s2survey/log/n-error.log;

        # Forbidden directories
        location ~ ^/(inc|lib|system)/ {
                deny all;
                return 403;
        }
        location ~ ^/files/(protected|upload|share)/ {
                deny all;
                return 403;
        }
        location ~ ^/download/(release|system|working)/ {
                deny all;
                return 403;
        }
        
        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        location ~ /\.ht {
                deny all;
                return 403;
        }
        
        # Use default favicon, so no entries will appear in the errorlog
        location ~ ^/.*/favicon\.ico$ {
                try_files /favicon.ico =404;
        }

        # Munin
        location ^~ /munin/static/ {
                alias /etc/munin/static/;
                expires modified +1w;
        }

        location ^~ /munin/ {
                alias /var/cache/munin/www/;
                expires modified +310s;
                try_files $uri $uri/ =404;
                auth_basic "Munin";
                auth_basic_user_file /etc/munin/munin-htpasswd;
        }
        
        # Monit via SSL
        location ^~ /monit/ {
                rewrite ^/monit/(.*) /$1 break;
                proxy_ignore_client_abort on;
                proxy_pass   http://127.0.0.1:2812;
                proxy_redirect  http://127.0.0.1:2812 /monit;
                proxy_cookie_path / /monit/;
        }
        
        # PHP FPM status
        location ~ ^/(status\.primary|ping\.primary)$ {
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_pass unix:/run/php/php-fpm.sock;
                log_not_found off;
                allow 95.130.22.98;
                allow 2a02:2940:0:c007::98;
                allow 127.0.0.1;
                allow ::1;
                deny all;
        }
        location ~ ^/(status\.secondary|ping\.secondary)$ {
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_pass 127.0.0.1:9002;
                log_not_found off;
                allow 95.130.22.98;
                allow 2a02:2940:0:c007::98;
                allow 127.0.0.1;
                allow ::1;
                deny all;
        }
        location ~ ^/nginx_status$ {
                stub_status;
                log_not_found off;
                allow 95.130.22.98;
                allow 2a02:2940:0:c007::98;
                allow 127.0.0.1;
                allow ::1;
                deny all;
        }
        
        # Different PHP settings for the sosci administration
        location ~ ^/admin/(.+\.php)$ {
                # Allow for a burst of 100 requests (limit is still 10/s) to allow for 100 images from protected storage
                limit_req zone=php burst=100;
                try_files $uri =404;
                # No secondary pool for that
                fastcgi_pass unix:/run/php/php-fpm.sock;
                fastcgi_index index.php;
                fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Special
                fastcgi_param PHP_VALUE "
                        max_execution_time = 20
                        memory_limit = 256M
                        max_file_uploads = 50
                ";
        }

        # pass the survey's PHP scripts to FastCGI server listening on 127.0.0.1:9000
        # location ~ (/index.php|^/(help|shop)/(.+\.php)|^/images/wedge.php|^/spellcheck.php|^/download/(activate|download|report)\.php|^/homepage/admin/(.+\.php)|^/tools/view-chars.php)$ {
        location ~ (/index.php|^/images/wedge.php|^/spellcheck.php)$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                # First, you need to capture fastcgi errors
                fastcgi_intercept_errors      on;
                # Specifies in which cases a request should be passed to the next server
                fastcgi_next_upstream         error timeout http_500 http_503;
                # Limits the time during which a request can be passed
                # to the next server
                fastcgi_next_upstream_timeout 10s;
                # Limits the number of possible tries for passing
                # a request to the next server
                fastcgi_next_upstream_tries   2;
                # Upstream to pass request to
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 32M
                ";
        }

        # Deny all PHP files that are not index.php or /admin/       
        location ~ \.php$ {
                access_log /var/www/s2survey/log/n-forbidden.log;
                error_log /var/www/s2survey/log/n-forbidden.log;
                if (-f $request_filename) {
                     return 403;
                }
                if (!-f $request_filename) {
                     return 404;
                }
        }


        # Docs
        location /doc/ {
                alias /usr/share/doc/;
                autoindex on;
                allow 127.0.0.1;
                allow 95.130.22.100;
                deny all;
        }

        # Cache management
        location ~* \.(?:ico|css|js|svg|gif|jpg|jpeg|png|flv|pdf|swf)$ {
                expires 1d;
                add_header Vary Accept-Encoding;
                add_header X-Content-Type-Options nosniff;
                access_log off;
                error_log off;
        }

        # Anything else
        location ~ / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404; #$uri/ /index.php /index.html =404;
        }

}
</file>

<file conf /etc/nginx/sites-available/02.customers.conf>
server {
        listen 95.130.22.101:80;
        listen [2a02:2940:0:c007::101]:80;
        server_name
                access.lgbti-label.ch
                verbaendeumfrage2019.divv.de;

        # Allow certbot auth
        root     /var/www/s2survey/certbot;
        location ~ ^/.well-known/ {
        }

        # redirect everything else
        location ~ / {
                # add_header X-Debug "$host";
                rewrite        ^ https://$host$request_uri? permanent;
        }

        access_log /var/www/s2survey/log/n-access-custom.log noagent;
        error_log /var/www/s2survey/log/n-error-custom.log;
}

server {
        listen 95.130.22.101:443 ssl http2;
        listen [2a02:2940:0:c007::101]:443 ssl http2;
        server_name access.lgbti-label.ch;

        root /var/www/s2survey/html/_$host;
        index index.php index.html index.htm;
        client_max_body_size    64M;

        access_log /var/www/s2survey/log/n-access-custom.log noagent;
        error_log /var/www/s2survey/log/n-error-custom.log;

        ssl_certificate_key /etc/letsencrypt/live/access.lgbti-label.ch/privkey.pem;
        ssl_certificate /etc/letsencrypt/live/access.lgbti-label.ch/fullchain.pem;

        location ~ /\.ht {
                deny all;
                return 403;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                fastcgi_intercept_errors      on;
                fastcgi_next_upstream         error timeout http_500 http_503;
                fastcgi_next_upstream_timeout 10s;
                fastcgi_next_upstream_tries   2;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 16M
                ";
        }

        # Cache management
        location ~* \.(?:ico|css|js|gif|jpg|jpeg|png|flv|pdf|swf)$ {
                expires 1d;
                add_header Vary Accept-Encoding;
                add_header X-Content-Type-Options nosniff;
                # access_log off;
        }

        # Anything else
        location ~ / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404; #$uri/ /index.php /index.html =404;
        }

}
</file>

<file conf /etc/nginx/sites-available/06.redirect.conf>
# www.soscisurvey.de
# HTTP to HTTPS redirects
server {
        listen          80;
        listen          [::]:80;
        server_name     soscisurvey.de www.soscisurvey.de soscisurvey.com www.soscisurvey.com soscisurvey.net www.soscisurvey.net admin.soscisurvey.de;

        # Allow certbot auth
        root     /var/www/s2survey/certbot;
        location ~ ^/.well-known/ {
                # Simply serve the file
        }

        # redirect everything else
        location ~ / {
                # add_header X-Debug "$host";
                rewrite        ^ https://$host$request_uri? permanent;
        }

        # rewrite         ^ https://$server_name$request_uri? permanent;
}

# (Sub-)Domains to be redirected
server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name soscisurvey.de admin.soscisurvey.de;

        # ssl on;
        # ssl_certificate_key /var/www/ssl/www.soscisurvey.2019.key;
        # ssl_certificate /var/www/ssl/www.soscisurvey.2019.crt;
        ssl_certificate_key /etc/letsencrypt/live/soscisurvey.de/privkey.pem;
        ssl_certificate /etc/letsencrypt/live/soscisurvey.de/fullchain.pem;

        # Allow certbot auth
        root     /var/www/s2survey/certbot;
        location ~ ^/.well-known/ {
                # Simply serve the file
        }

         # redirect everything else
        location ~ / {
                rewrite         ^ https://www.soscisurvey.de$request_uri? permanent;
        }
}

# soscisurvey.com + soscisurvey.net (redirect)
server {
        listen          443 ssl http2;
        listen          [::]:443 ssl http2;
        server_name     soscisurvey.com www.soscisurvey.com soscisurvey.net www.soscisurvey.net; # 95.130.22.98 95.130.22.99;

        # Allow certbot auth
        root     /var/www/s2survey/certbot;
        location ~ ^/.well-known/ {
                # Simply serve the file
        }

         # redirect everything else
        location ~ / {
                rewrite         ^ https://www.soscisurvey.de/?l=eng permanent;
        }

        # ssl_certificate /var/www/ssl/www.soscisurvey.2019.crt;
        # ssl_certificate_key /var/www/ssl/www.soscisurvey.2019.key;
        ssl_certificate_key /etc/letsencrypt/live/soscisurvey.de/privkey.pem;
        ssl_certificate /etc/letsencrypt/live/soscisurvey.de/fullchain.pem;
}
</file>

<file conf /etc/nginx/sites-available/06.redirect.conf>
# s2survey.net
# HTTP to HTTPS redirects
server {
        listen 80;
        listen [::]:80;
        server_name     s2survey.net www.s2survey.net s2survey.com www.s2survey.com s2survey.de www.s2survey.de;
        rewrite         ^ https://$server_name$request_uri? permanent;
}

# (Sub-)Domains to be redirected
server {
        listen 443 ssl http2;
        listen [2a02:2940:0:c007::101]:443 ssl http2;

        server_name www.s2survey.net s2survey.com www.s2survey.com;

        # The server certificate must appear before the chained certificates in the combined file
        # cat www.example.com.crt bundle.crt > www.example.com.chained.crt
        ssl_certificate_key /etc/letsencrypt/live/s2survey.net/privkey.pem; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/s2survey.net/fullchain.pem; # managed by Certbot

        rewrite         ^ https://s2survey.net$request_uri? permanent;



}

# (Sub-)Domains to be redirected
server {
        listen 443 ssl http2;
        listen [2a02:2940:0:c007::101]:443 ssl http2;

        server_name www.s2survey.de;

        ssl_certificate_key /etc/letsencrypt/live/s2survey.net/privkey.pem; # managed by Certbot
        ssl_certificate /etc/letsencrypt/live/s2survey.net/fullchain.pem; # managed by Certbot

        rewrite         ^ https://s2survey.de$request_uri? permanent;

}
</file>

<file conf /etc/nginx/sites-available/09.nossl.s2survey.conf>
# Allows access without SSL for single projects
server {
        listen 80;
        listen [::]:80;

        root /var/www/soscisurvey/html;
        index index.php index.html index.htm;
        error_page 404 = /spellcheck.php;

        server_name nossl.s2survey.net;

        access_log /var/www/soscisurvey/log/nossl-access.log noip;
        error_log /var/www/soscisurvey/log/nossl-error.log;


        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        location ~ /\.ht {
                deny all;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                fastcgi_intercept_errors      on;
                fastcgi_next_upstream         error timeout http_500 http_503;
                fastcgi_next_upstream_timeout 10s;
                fastcgi_next_upstream_tries   2;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 16M
                ";
        }
        
        # Allowed directories
        location /images/ {
                try_files $uri =404;
                allow all;
        }
        location /view/ {
                try_files $uri =404;
                allow all;
        }

        # Allowed projects
        location /curtest/ {
                try_files $uri $uri/ =404;
                allow all;
        }

        location / {
                allow 127.0.0.1;
                deny all;
                try_files $uri $uri/ =404;
        }
}
</file>

<file conf /etc/nginx/sites-available/default>
server {
        listen 80;
        listen [::]:80;

        root /var/www/default/html;
        index index.php index.html index.htm;

        # Make site accessible from http://localhost/
        server_name localhost;

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        location ~ /\.ht {
                deny all;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
        }

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404;
                # Uncomment to enable naxsi on this location
                # include /etc/nginx/naxsi.rules
        }

        # Server status
        location = /server-status {
                # copied from http://blog.kovyrin.net/2006/04/29/monitoring-nginx-with-rrdtool/
                stub_status on;
                #extended_status on;
                access_log   off;
                allow 127.0.0.1;
                allow 95.130.22.98;
                deny all;
        }

        # Server status for munin
        location = /nginx_status {
                stub_status on;
                access_log   off;
                allow 127.0.0.1;
                allow 95.130.22.98;
                deny all;
        }
        
        # Also access munin via localhost
        location ^~ /munin/static/ {
                alias /etc/munin/static/;
                expires modified +1w;
        }

        location ^~ /munin/ {
                alias /var/cache/munin/www/;
                expires modified +310s;
                try_files $uri $uri/ =404;
                auth_basic "Munin";
                auth_basic_user_file /etc/munin/munin-htpasswd;
        }

}
</file>

Dann die Seiten noch aktivieren (nur bei der alten Lösung über ''sites-available'')

<code bash>
sudo ln -s /etc/nginx/sites-available/01.s2survey.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/02.customers.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/06.redirect.conf /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/09.nossl.s2survey.conf /etc/nginx/sites-enabled/
</code>


===== E-Mail =====

Für die eingehenden E-Mails ist ein gesonderter Server zuständig, konfiguriert nach [[https://thomas-leister.de/mailserver-debian-stretch/|thomas-leister.de]].

Die Server-SSL-Schlüssel können leider nicht direkt für TLS verwendet, werden also erstmal neue erstellen.

<code>
sudo openssl req -new -x509 -days 3650 -nodes -out /etc/ssl/certs/postfix.pem -keyout /etc/ssl/private/postfix.key

Country Name (2 letter code) [AU]:                            DE
State or Province Name (full name) [Some-State]:              Bayern
Locality Name (eg, city) []:                                  Muenchen
Organization Name (eg, company) [Internet Widgits Pty Ltd]:   SoSci Survey GmbH
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:               s2survey.net
Email Address []:                                             info@soscisurvey.de
</code>

Damit es mit ausgehenden E-Mails und der Erkennung von Mail-Rückläufern funktioniert, sind folgende Änderungen erforderlich.

**Hinweis:** Auf www.soscisurvey.de werden die Rückläufer nicht direkt verarbeitet, sondern stattdessen von ''mail.soscisurvey.de:143''. Das ist der unverschlüsselte IMAP-Port.


==== Postfix ====

<file conf /etc/postfix/main.cf>
##### Use Maildir for IMAP/dovecot #####
home_mailbox = Maildir/

##### TLS settings ######
# see https://thomas-leister.de/erweiterte-postfix-ssltls-konfiguration-verschluesselte-server-server-verbindungen/

tls_ssl_options = NO_COMPRESSION
tls_high_cipherlist=EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA

### outgoing connections ###
smtp_tls_security_level=may
smtp_tls_cert_file=/etc/ssl/certs/postfix.pem
smtp_tls_key_file=/etc/ssl/private/postfix.key
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtp_bind_address = 95.130.22.100
smtp_bind_address6 = 2a02:2940:0:c007::100

### incoming connections ###
smtpd_sasl_security_options = noanonymous
smtpd_use_tls=yes
smtpd_tls_security_level=may
smtpd_tls_cert_file=/etc/ssl/certs/postfix.pem   # Hier werden die SSL-Zertifikate des Webservers verwendet!
smtpd_tls_key_file=/etc/ssl/private/postfix.key
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3
smtpd_tls_mandatory_ciphers = high
smtpd_tls_exclude_ciphers = ECDHE-RSA-RC4-SHA
smtpd_tls_mandatory_exclude_ciphers = ECDHE-RSA-RC4-SHA

# Allow 64 MB message size
message_size_limit = 67108864

# email accounts (hosting)
virtual_alias_domains = s2survey.de, www.s2survey.de, s2survey.com, www.s2survey.com
virtual_alias_maps = hash:/etc/postfix/virtual

# sender aliases
sender_canonical_maps=hash:/etc/postfix/senders

# Limit connections per server (default 20)
smtp_destination_concurrency_limit = 3

# Stop retries after 36 hours (to quickly delete problem-mails)
bounce_queue_lifetime = 24h
maximal_queue_lifetime = 36h

# Basic settings
myhostname = s2survey.net
mydestination = $myhostname, mail.s2survey.net, www.s2survey.net, soscisurvey2, localhost.localdomain, localhost
# inet_protocols = ipv4
inet_protocols = all

# OpenDKIM (optional)
</file>

<file conf /etc/postfix/senders>
root      info@s2survey.net
www-data  info@s2survey.net
</file>

<file conf /etc/postfix/virtual>
root                         info@soscisurvey.de
webmaster                    info@soscisurvey.de
postmaster                   info@soscisurvey.de
root@mail.s2survey.net       info@soscisurvey.de

mailer@s2survey.net          mailer
survey@s2survey.net          mailer
noreply@soscisurvey.de       mailer

info@s2survey.net            info@soscisurvey.de
info@s2survey.com            info@soscisurvey.de
info@s2survey.de             info@soscisurvey.de

hostmaster@s2survey.net      info@soscisurvey.de
hostmaster@www.s2survey.net  info@soscisurvey.de
hostmaster@s2survey.com      info@soscisurvey.de
hostmaster@www.s2survey.com  info@soscisurvey.de
hostmaster@s2survey.de       info@soscisurvey.de
hostmaster@www.s2survey.de   info@soscisurvey.de
webmaster@s2survey.net       info@soscisurvey.de
webmaster@www.s2survey.net   info@soscisurvey.de
webmaster@s2survey.com       info@soscisurvey.de
webmaster@www.s2survey.com   info@soscisurvey.de
webmaster@s2survey.de        info@soscisurvey.de
webmaster@www.s2survey.de    info@soscisurvey.de
postmaster@s2survey.net      info@soscisurvey.de
postmaster@www.s2survey.net  info@soscisurvey.de
postmaster@s2survey.com      info@soscisurvey.de
postmaster@www.s2survey.com  info@soscisurvey.de
postmaster@s2survey.de       info@soscisurvey.de
postmaster@www.s2survey.de   info@soscisurvey.de

noreply@s2survey.net         info@soscisurvey.de
wiki@s2survey.net            info@soscisurvey.de
root@s2survey.net            info@soscisurvey.de
www-data@s2survey.net        info@soscisurvey.de
</file>

Wir sorgen noch dafür, dass die Mail-(Adressen-)Logs nicht in die `syslog kommen`.

<code bash>
sudo vim /etc/rsyslog.d/50-default.conf
sudo vim /etc/rsyslog.conf
# Ergänzen mail.none oder nur .none
*.*;mail.none,auth,authpriv.none   -/var/log/syslog
</code>

Für die Rückläufer-Mails benötigen wir außerdem noch einen Nutzer für das Mail-Postfach mit Passwort.

<code bash>
sudo adduser mailer --shell /sbin/nologin
</code>

Zuletzt noch DKIM einrichten, damit Mails von @s2survey.net nirgends hängen bleiben.

Hierzu folgende Anleitung: [[https://kofler.info/dkim-konfiguration-fuer-postfix/|DKIM für Postfix]].

<code bash>
sudo mkdir /etc/opendkim
sudo mkdir /etc/opendkim/keys
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod go-rw /etc/opendkim/keys
</code>

<file conf /etc/opendkim.conf>
UserID                  opendkim:opendkim
UMask                   002
PidFile                 /var/run/opendkim/opendkim.pid
Socket                  inet:8892@localhost
AutoRestart             yes
AutoRestartRate         10/1h
# Später weniger Logging
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
# Nicht zu streng sein
Canonicalization        relaxed/simple
# interne Mails nicht mit OpenDKIM verarbeiten
ExternalIgnoreList      refile:/etc/opendkim/trusted
InternalHosts           refile:/etc/opendkim/trusted
# Keys pro Domain
# (refile: für Dateien mit regulären Ausdrücke)        
SigningTable            refile:/etc/opendkim/signing.table
KeyTable                /etc/opendkim/key.table       
# diesen Signatur-Algorithmus verwenden
SignatureAlgorithm      rsa-sha256
# Always oversign From (sign using actual From and a null From to prevent
# malicious signatures header fields (From and/or others)
OversignHeaders         From
</file>

<file conf /etc/opendkim/trusted>
127.0.0.1
::1
localhost
soscisurvey2
s2survey
s2survey.net
</file>

<file conf /etc/opendkim/signing.table>
# Nur Mails @s2survey.net signieren
*@s2survey.net s2survey
</file>

<file conf /etc/opendkim/key.table>
s2survey s2survey.net:2019a:/etc/opendkim/keys/2019a.private
</file>

<code bash>
sudo su
cd /etc/opendkim/keys
opendkim-genkey -d s2survey.net -b 3072 -r -s 2019a
chown opendkim:opendkim *
less 2019a.txt
</code>

Einen DNS-Eintrag für s2survey.net erzeugen (z.B. ''2019a._domainkey.s2survey.net''), entsprechend dem Inhalt der Datei ''2019a.txt''. Dabei kommt alles zwischen den Klammern in den TXT Eintrag, aber Anführungszeichen, Leerzeichen und Zeilenumbrüche werden entfernt.

<code bash>
exit
sudo systemctl restart opendkim
opendkim-testkey -d s2survey.net -s 2019a -vvv
</code>

<file conf /etc/postfix/main.cf>
# OpenDKIM
milter_default_action = accept   
milter_protocol   = 6              
smtpd_milters     = inet:localhost:8892
non_smtpd_milters = inet:localhost:8892
</file>


==== Dovecot ====

<file conf /etc/dovecot/conf.d/10-auth.conf>
disable_plaintext_auth = yes
</file>

Testen der Verbindung

<code bash>
echo "Subject: E-Mail-Test" | sendmail -v mailer
echo "Subject: E-Mail-Test" | sendmail -v info@soscisurvey.de
printf "From:info@soscisurvey.de\nSubject: E-Mail-Test" | sendmail -v info@soscisurvey.de
</code>

<code bash>
openssl s_client -crlf -connect localhost:993

A1 login mailer [PASSWORD_MAILER]
n namespace
A status INBOX (messages)
g21 SELECT "INBOX"
f fetch 1:1 (BODY[HEADER.FIELDS (Subject)])
</code>

Oder auch mit TLS für STMP, vgl. http://domainhostseotool.com/how-to-use-openssl-to-send-email-via-smtp-server-that-supports-starttls.html

<code bash>
openssl s_client -starttls smtp -connect smtp.example.com:587
</code>


===== Webserver (nginx) ======

s. auch https://www.digitalocean.com/community/tutorials/how-to-set-up-nginx-with-http-2-support-on-ubuntu-16-04

==== SSL ====

Wird generell in ''nginx'' konfiguriert. Allerdings ist sicherzustellen, dass die Schlüssel unter ''/var/www/s2survey/ssl'' vorhanden sind und dass die richtigen Prozesse auf diese zugreifen können.

<code bash>
sudo chown -R root:ssl-cert /var/www/ssl
</code>

===== Reload I =====

<code bash>
sudo systemctl stop php7.4-fpm mysql nginx sshd
sudo systemctl start php7.4-fpm mysql nginx sshd
</code>


===== SSL-Zertifikate =====

Die Haupt-Domains der SoSci Survey GmbH sind www.soscisurvey.de und s2survey.net - nur die Haupt-Domains werden durch ein EV-Zertifikat abgesichert. Für die Neben-Domains (mit/ohne www, TLDs .com, .net., .de) wird Let's Encrypt verwendet. Diese Neben-Domains dienen vor allem der Weiterleitung.

Organization Validation (OV) Zertifikat

  * %%www.soscisurvey.de%% (primäre Domain)
  * %%s2survey.net%% (Pro-Server Survey)
  * %%admin.s2survey.net%% (Pro-Server Verwaltung)


Let's Encrypt für Weiterleitungen

  * %%www.s2survey.net%% -> %%s2survey.net%%
  * %%s2survey.com%% -> %%s2survey.net%%
  * %%www.s2survey.com%% -> %%s2survey.net%%
  * %%s2survey.de%% -> unklar, ob aktiv
  * %%www.s2survey.de%% -> unklar, ob aktiv
  * %%soscisurvey.de%% -> %%www.soscisurvey.de%%
  * %%soscisurvey.com%% -> %%www.soscisurvey.de/?l=eng%%
  * %%www.soscisurvey.com%% -> %%www.soscisurvey.de/?l=eng%%
  * %%soscisurvey.net%% -> %%www.soscisurvey.de/?l=eng%%
  * %%www.soscisurvey.net%% -> %%www.soscisurvey.de/?l=eng%% (evtl. überflüssig)
  * %%admin.soscisurvey.de%% (Administration, explizit nicht premium abgesichert)
  * %%95.130.22.98%% -> no more
  * %%95.130.22.99%% -> no more

Bei den Weiterleitungen ist für Port 80 (HTTP) und Pfad ''.well-known'' eine Umleitung auf das Verzeichnis ''/var/www/s2survey/certbot'' eingebaut. Dadurch kann für alle Domains ein einheitlicher Webroot verwendet werden.

<code bash>
sudo certbot certonly --webroot --debug-challenges --dry-run -w /var/www/s2survey/certbot -d soscisurvey.de -d soscisurvey.com -d www.soscisurvey.com -d soscisurvey.net -d www.soscisurvey.net

**TODO:** -d admin.soscisurvey.de

sudo certbot certonly --webroot --debug-challenges --dry-run -w /var/www/s2survey/certbot -d www.s2survey.net -d s2survey.net -d s2survey.com -d www.s2survey.com -d s2survey.de -d www.s2survey.de

sudo certbot certonly --webroot -w /var/www/s2survey/certbot -d DOMAINNAME
</code>
===== SoSci Survey installieren =====

  * Dateien kopieren
  * Dateirechte anpassen
  * Datenbank initialisieren ''admin/install.php''
  * Korrekte Funktion prüfen
  * Serienmail-Versand testen

<code bash>
cd /var/www/s2survey/html/

sudo chown -R www-ftp:www-data admin images inc lib modules plugins script templates
sudo chmod -R 750 admin images inc lib modules plugins script templates
# system wird erst von der Installationsroutine angelegt
# sudo chown -R www-data:www-ftp system
# sudo chmod -R 770 system

sudo chown -R www-ftp:nginx admin images inc lib modules plugins script templates
sudo chown -R www-ftp:www-data admin images inc lib modules plugins script templates
sudo chmod -R 750 admin images inc lib modules plugins script templates
</code>

Die Verzeichnisse ''files'' und ''view'' sollten automatisch passende Rechte bekommen, wenn SoSci Survey sie anlegt (''www-data''). Bei ''system'' nochmal kontrollieren.


==== SoSci Survey ====

Zwei Scripte (gehören ''www-data''), werden beim Übertragen der Inhalte evtl. nicht automatisch kopiert (permission denied).

<code bash>
cd /var/www/s2survey
sudo mkdir script
sudo vim script/crontask.sh
sudo vim script/cronjob.sh
sudo chown www-data:www-data script/crontask.sh
sudo chown www-data:www-data script/cronjob.sh
sudo chmod u+x script/crontask.sh
sudo chmod u+x script/cronjob.sh

sudo chown www-data:www-data script
sudo chown www-data:www-data log
sudo chmod 700 script
sudo chmod 700 log
</code>

<file bash /var/www/s2survey/script/crontask.sh>
#!/bin/bash
cd /var/www/s2survey/html/admin/
/usr/bin/php /var/www/s2survey/html/admin/crontask.php password=123456
</file>

<file bash /var/www/s2survey/script/cronjob.sh>
#!/bin/bash
cd /var/www/s2survey/html/admin/
/usr/bin/php /var/www/s2survey/html/admin/cronjob.php password=123456
</file>




===== Cronjobs =====

Falls die Systemuhr UTC läuft, dann entsprechend alles 2 Stunden früher.

<code>
sudo crontab -e

# m     h       dom     mon     dow     command
@reboot                                 /root/mailinfo.reboot.sh
15      00      *       *       *       /etc/cron.daily/chkrootkit > /var/log/chkrootkit/daily.log
30      00      *       *       *       /etc/cron.daily/rkhunter
00      01      *       *       *       /root/backup/backup-daily.sh
00      02      *       *       sun     /root/backup/backup-weekly.sh
30      04      *       *       sun     /root/mailinfo.update.sh

00      05      *       *       sun     /usr/bin/certbot renew --post-hook "service nginx reload"
55      05      *       *       sun     /sbin/reboot
</code>

<code>
sudo crontab -e -uwww-data

# m     h       dom     mon     dow     command
*/1     *       *       *       *       /var/www/s2survey/script/crontask.sh >/dev/null 2>&1
0      3       *       *       *       /var/www/s2survey/script/cronjob.sh
</code>


===== Überwachung =====


==== Munin ====

<code bash>
sudo ln -s /usr/share/munin/plugins/nginx_request /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/nginx_status /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/http_loadtime /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/iostat /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/iostat_ios /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/postfix_mailqueue /etc/munin/plugins/
sudo ln -s /usr/share/munin/plugins/postfix_mailvolume /etc/munin/plugins/
</code>

Noch ein Extra-PlugIn für PHP-FPM

<code bash>
cd /usr/share/munin/plugins/
sudo wget -O php-fpm https://raw.github.com/MorbZ/munin-php-fpm/master/php-fpm.php
sudo chmod +x php-fpm
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-memory
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-cpu
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-count
sudo ln -s /usr/share/munin/plugins/php-fpm /etc/munin/plugins/php-fpm-time
sudo service munin-node restart
</code>


<code bash>
sudo vim /etc/munin/munin.conf

> [localhost.localdomain]
< [s2survey.net]
</code>

<code bash>
sudo htpasswd -c /etc/munin/munin-htpasswd admin
</code>

Zum Testen eines PlugIns.

<code bash>
sudo munin-run nginx_status
</code>


==== Monit ====

<code bash>
# PHP-Service herausfinden
systemctl list-unit-files | grep enabled
sudo vim /etc/monit/conf.d/s2survey.conf
# u.s.w.
</code>

<file conf /etc/monit/conf.d/s2survey.conf>
set daemon 20 START DELAY 120
set mail-format { from: monit@s2survey.net }
set eventqueue basedir /var/monit/ slots 1000
set mmonit https://monit:XXXXXXXXXXX@mail.soscisurvey.de/mmonit/collector
set httpd
        port 2812
        allow admin:"XXXXXXXXXXX"
</file>

Den Namen des PID-File muss man zunächst in MySQL ermitteln:

<code mysql>
show variables like '%pid%';
</code>

<file conf /etc/monit/conf.d/mysql.conf>
check process mysqld with pidfile /run/mysqld/mysqld.pid
    start program = "/usr/sbin/service mysql start"
    stop program = "/usr/sbin/service mysql stop"
    if failed host 127.0.0.1 port 3306
        for 3 cycles
        then restart
    if 3 restarts within 20 cycles
        then timeout
</file>

<file conf /etc/monit/conf.d/nginx.conf>
check process nginx with pidfile /var/run/nginx.pid
   start program = "/usr/sbin/service nginx start" with timeout 60 seconds
   stop program  = "/usr/sbin/service nginx stop"
   if failed port 80 for 2 cycles
     then restart
   if cpu > 50% for 2 cycles
     then alert
</file>

<file conf /etc/monit/conf.d/php-fpm.conf>
check process php-fpm with pidfile /var/run/php/php8.1-fpm.pid
        group php-www
        start program = "/bin/systemctl start php8.1-fpm"
        stop program = "/etc/monit/php-fpm.stop.sh"
        if failed unixsocket /var/run/php/php-fpm.sock
                then alert
        if failed unixsocket /var/run/php/php-fpm.sock
                for 2 times within 3 cycles
                then restart
        if failed unixsocket /var/run/php/php-fpm.sock
                for 5 cycles
                then exec "/sbin/reboot"
        if 5 restarts within 8 cycles
                then alert
</file>

<file conf /etc/monit/conf.d/fail2ban.conf>
check process fail2ban with pidfile /var/run/fail2ban/fail2ban.pid
  group services
  start program = "/bin/systemctl start fail2ban"
  stop  program = "/bin/systemctl stop fail2ban"
  if failed unixsocket /var/run/fail2ban/fail2ban.sock then restart
  if 5 restarts within 5 cycles then timeout

check file fail2ban_log with path /var/log/fail2ban.log
    if match "ERROR|WARNING" then alert
</file>

<file conf /etc/monit/conf.d/system.conf>
check system [DOMAINNAME]
   group resources
   if memory usage > 75%
      for 5 cycles then alert
   else if passed
      within 5 cycles
      then alert
   if loadavg (15min) is greater than 1.5
      for 5 cycles then alert
   else if passed
      within 5 cycles
      then alert       
   if cpu usage (user) > 75%
        for 2 cycles
        then alert
   if cpu usage (system) > 25%
        for 5 cycles
        then alert
   if cpu usage (wait) > 50%
        for 5 cycles
        then alert

check filesystem rootfs with path /
        if space usage > 75% then alert
check device bootfs with path /boot
        if space usage > 75% then alert
</file>

<file conf /etc/monit/conf.d/sosci.conf>
# Website must be accessible
CHECK HOST sosci WITH ADDRESS 95.130.22.100
  START PROGRAM = "/etc/monit/webserver.start.sh"
  STOP PROGRAM  = "/etc/monit/webserver.stop.sh"
  # IF NOT EXIST THEN ALERT
   # if failed (url https://www.soscisurvey.de/example/?debug&password=demo and content == 'onlineFragebogen' and timeout 20 seconds)
   #   then alert
  IF FAILED (url https://s2survey.net/admin/?stay and content == 'SoSci Survey' and timeout 20 seconds)
    FOR 2 CYCLES
    THEN RESTART
  IF FAILED (url https://s2survey.net/admin/?stay and content == 'SoSci Survey' and timeout 20 seconds)
    FOR 4 CYCLES
    THEN EXEC "/sbin/reboot"

# Check SSL certificate
  IF FAILED (port 443 protocol https AND certificate valid > 30 days)
    THEN ALERT
    
# Crontask must run every minute
CHECK FILE crontask WITH PATH /var/www/s2survey/html/system/logfiles/crontask.log
    IF DOES NOT EXIST THEN ALERT
    IF timestamp > 20 minutes THEN ALERT

# Cronjob must run daily
CHECK FILE cronjob WITH PATH /var/www/s2survey/html/system/logfiles/cronjob.log
    IF DOES NOT EXIST THEN ALERT
    IF timestamp > 25 hours THEN ALERT
</file>

<file bash /etc/monit/php-fpm.stop.sh>
#!/bin/bash

# Stop php-fpm
/bin/systemctl stop php8.1-fpm

# Kill any further instances of PHP-FPM
/usr/bin/pkill php8.1-fpm
</file>

<file bash /etc/monit/webserver.stop.sh>
#!/bin/bash

# Stop php-fpm
/etc/monit/php-fpm.stop.sh

# Stop nginx
/bin/systemctl stop nginx
</file>

<file bash /etc/monit/webserver.start.sh>
#!/bin/bash
/bin/systemctl start php7.4-fpm
/bin/systemctl start nginx
</file>

<file bash /etc/monit/webserver.restart.sh>
#!/bin/bash
/etc/monit/webserver.stop.sh
/etc/monit/webserver.start.sh
</file>

<file conf /etc/monit/conf.d/postfix>
CHECK PROCESS postfix WITH PIDFILE /var/spool/postfix/pid/master.pid
    start program = "/usr/sbin/service postfix start"
    stop program  = "/usr/sbin/service postfix stop"
    if cpu > 60% for 2 cycles then alert
    if cpu > 80% for 5 cycles then restart
    if failed host localhost port 25 type tcp protocol smtp
       with timeout 15 seconds
      then alert
    if 3 restarts within 5 cycles then timeout
</file>


<code bash>
sudo chmod u+x /etc/monit/php-fpm.stop.sh
sudo chmod u+x /etc/monit/webserver.stop.sh
sudo chmod u+x /etc/monit/webserver.start.sh
sudo chmod u+x /etc/monit/webserver.restart.sh
sudo monit -t
</code>


===== Reload II =====

<code bash>
sudo systemctl reload monit
sudo systemctl restart munin-node unattended-upgrades
</code>


===== Tests =====

==== Projekt anlegen ====

Dito

==== E-Mail ====

Für die ausgehenden E-Mails sind einige Anpassungen erforderlich, damit sie durch den Spamfilter kommen. Zu Testen mit [[https://www.mail-tester.com/|mail tester]].

Die TLS-Konfiguration kann man mit [[https://www.checktls.com/|checktls.com]] überprüfen.

Rückläufer über https://[SOSCI_URL]/admin/index.php?o=Receiver&a=bounces

Newsletter-Konfiguration: https://www.mail-tester.com/?lang=de

Gelegentlich mal Check gegen Blockhole-Listen: https://dnslytics.com/dns-blackhole-list


==== Backups ====

<code bash>
sudo su
duplicity collection-status file:///mnt/backup/soscisurvey2020/database
duplicity list-current-files file:///mnt/backup/soscisurvey2020/database
duplicity restore -t 2020-12-31T12:00:00-01:00 file:///mnt/backup/soscisurvey2020/database /var/backup/restore/
</code>


===== Sonstiges =====

  * Owncloud
  * Anleitung
  * SMS-Versand

==== Owncloud ====

  * Client auf allen PCs/Laptops abschalten
  * Auf dem neuen Server Logfile-Rotation aktivieren
  * Dateirechte prüfen nach dem Umzug
  * Cronjob für die Wartung ''sudo crontab -u www-data -e''
  
<code conf>
*/15 *    *     *   *    php /var/www/s2survey/html/owc/occ system:cron
</code>


===== Domain aufschalten =====

<code bash>
sudo su
cd /var/www/s2survey/html
vim VERZEICHNIS/index.php
# chdir() ergänzen (s. unten)

ln -s VERZEICHNIS _DOMAINNAME
vim /etc/nginx/sites-available/02.customers.conf
# (1) Domain oben im HTTP:80 ergänzen
# (2) Neuer Eintrag für die Domain (sonst müssten sie ein Zertifikat teilen)
#     => Aber erstmal mit Zertifikat von anderer Domain, sonst schlägt der Config-Test fehl
nginx -t
sudo systemctl reload nginx

certbot certonly --webroot --debug-challenges --dry-run -w /var/www/s2survey/certbot -d DOMAINNAME
certbot certonly --webroot -w /var/www/s2survey/certbot -d DOMAINNAME
</code>

<code php>
<?PHP
// change to the correct working directory
chdir('../PROJECT_DIR/');
// Start or continue interview
require('../inc/Interviewer.php');
Interviewer::run(PROJECT_ID);
?>
</code>

<code conf>
server {
        listen 95.130.22.101:443 ssl http2;
        listen [2a02:2940:0:c007::101]:443 ssl http2;
        server_name DOMAINNAME;

        root /var/www/s2survey/html/_$host;
        index index.php index.html index.htm;
        client_max_body_size    64M;

        access_log /var/www/s2survey/log/n-access-custom.log noagent;
        error_log /var/www/s2survey/log/n-error-custom.log;

        ssl_certificate_key /var/www/ssl/s2survey.2019.key;
        ssl_certificate /var/www/ssl/s2survey.2019.crt;

        # ssl_certificate_key /etc/letsencrypt/live/DOMAINNAME/privkey.pem; # managed by Certbot
        # ssl_certificate /etc/letsencrypt/live/DOMAINNAME/fullchain.pem; # managed by Certbot


        location ~ /\.ht {
                deny all;
                return 403;
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        location ~ \.php$ {
                limit_req zone=php burst=50;
                try_files $uri =404;

                fastcgi_intercept_errors      on;
                fastcgi_next_upstream         error timeout http_500 http_503;
                fastcgi_next_upstream_timeout 10s;
                fastcgi_next_upstream_tries   2;
                fastcgi_pass sosci;

                fastcgi_index index.php;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
                include fastcgi_params;
                # Limitations for survey projects
                fastcgi_param PHP_VALUE "
                        max_execution_time = 5
                        memory_limit = 64M
                        max_file_uploads = 5
                        post_max_size = 16M
                ";
        }

        # Cache management
        location ~* \.(?:ico|css|js|gif|jpg|jpeg|png|flv|pdf|swf)$ {
                expires 1d;
                add_header Vary Accept-Encoding;
                add_header X-Content-Type-Options nosniff;
                # access_log off;
        }

        # Anything else
        location ~ / {
                # First attempt to serve request as file, then
                # as directory, then fall back to index.html
                try_files $uri $uri/ =404;
        }
}
</code>

==== Let's Encrypt Domain entfernen ====

Wenn man das Zertifikat nicht mehr braucht...

<code bash>
sudo certbot delete --cert-name mobibo2020.de
</code>

Vorher bei df.eu prüfen, ob die Domain wirklich abbestellt wurde.


===== Sonstiges =====


==== CSR für SSL =====

<code bash>
cd /var/www/ssl

# Schlüssel erzeugen:
sudo su
openssl genrsa 4096 > s2survey.2019.key
# Certificate Request
openssl req -new -key s2survey.2019.key -out s2survey.2019.csr

Country Name (2 letter code) [AU]:DE
State or Province Name (full name) [Some-State]:Bayern
Locality Name (eg, city) []:Muenchen
Organization Name (eg, company) [Internet Widgits Pty Ltd]:SoSci Survey GmbH
Organizational Unit Name (eg, section) []:Survey Hosting
Common Name (e.g. server FQDN or YOUR name) []:s2survey.net
Email Address []:info@soscisurvey.de

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
</code>


==== CSR für SSL (mehrere Domains) ====

Dafür brauchen wir eine Konfigurationsdatei.

<file conf s2survey.conf>
[req]
distinguished_name = req_distinguished_name
req_extensions     = req_ext
prompt             = no
 
[req_distinguished_name]
C  =  DE
ST = Bayern
L  = Muenchen
O = SoSci Survey GmbH
CN = www.soscisurvey.de
emailAddress = info@soscisurvey.de
 
[req_ext]
subjectAltName = @alt_names
[alt_names]
DNS.1   = s2survey.net
DNS.2   = admin.s2survey.net
</file>

<code>
sudo openssl req -out s2survey.2022.csr -newkey rsa:4096 -nodes -keyout s2survey.2022.key -config s2survey.conf
</code>


==== Backup-Schlüssel aktualisieren ====

Neuen Schlüssel erzeugen.

<code bash>
cd ~
sudo su
gpg --gen-key
 
gpg --list-secret-keys
gpg --output private.pem --armor --export-secret-key [KEYID]
gpg --output public.pgp --armor --export [KEYID]
chown sosci:sosci private.pem
 
# Sichern via FTP + RAM Disk + KeePass
 
rm private.pem
rm public.pgp
</code>

Passwort ohne Anführungszeichen verwenden!

Die letzten Datenbank-Backups auslesen und auf die lokale Platte kopieren (wobei das über `duplicity` läuft und nicht über `duply`).

In der `duply` Konfiguration austauschen:

  * Schlüssel
  * Passwort
  * Backup-Pfad (weil `duply` ja die alten Backups nicht mehr lesen/ergänzen/bereinigen kann)

Pfad in der ''/root/backup/backup-enc.sh'' aktualisieren.

<code bash>
vim /root/backup/backup-enc.sh
</code>

Alte Backups manuell löschen (einmal pro Woche nach dem Schlüsseltausch)

<code>
duplicity remove-older-than 31D --force file:///mnt/backup/soscisurvey2020/database
</code>


==== Passwort von GPG-Schlüssel ändern ====

<code bash>
gpg --list-secret-keys
gpg --edit-key [KeyID]
gpg> passwd
gpg> save
</code>


===== IPv6-Adresse =====

Die IP-Adresse wird entweder unter ''/etc/network/interfaces'' oder via NetworkManager (''nmtui'') oder via ''netplan'' festgelegt.

<code bash>
vim /etc/netplan/...
</code>
  
<file netplan /etc/netplan/00-installer-config.yaml>
network:
  ethernets:
    ens160:
      addresses:
      - 95.130.22.103/28
      - 2a02:2940:0:c007::103/64
      gateway4: 95.130.22.97
      gateway6: 2a02:2940:0:c007::1
      nameservers:
        addresses:
        - 95.130.16.100
        - 95.130.16.121
  version: 2
</file>

Mit Partnergate ist abgesprochen, dass die IPv6 ''2a02:2940:0:c007::102/64'' für den Server .102 verwendet wird u.s.w.

Der Gateway ist ''2a02:2940:0:c007::1''.

<code bash>
/sbin/ip -6 route show dev eth0
</code>

**Wichtig:** Vor dem Aktivieren von IPv6 auch die Firewall entsprechend anpassen.


==== NetworkManager ====

Einige Befehle für den NetworkManager:

  * ''nmcli device'' und ''nmcli connection''
  * ''ip --family inet6 addr show dev eth0 scope global''
  * ''nmcli device show eth0''
  * ''nmcli connection reload''
  * ''nmtui''
  * ''%%elinks http://www.wieistmeineip.de%%''
  * ''ping6 ipv6.google.com''
  * ''/etc/NetworkManager/system-connections/Wired connection 1''
  * ''/etc/NetworkManager/conf.d/99-local.conf'' ([[https://vk5tu.livejournal.com/57160.html|Activating IPv6 stable privacy addressing from RFC7217 ]])

<file ini /etc/NetworkManager/conf.d/99-local.conf>
[connection]
ipv6.ip6-privacy=0
ipv6.addr-gen-mode=stable-privacy
</file>

<file ini /etc/NetworkManager/system-connections/Wired connection 1>
[connection]
id=Wired connection 1
uuid=92b0a2d7-d185-4bf7-b3e9-8606a0e08355
type=ethernet
permissions=
timestamp=1574712975

[ethernet]
mac-address-blacklist=

[ipv4]
address1=95.130.22.102/28,95.130.22.97
dns=95.130.16.100;95.130.16.121;
dns-search=
method=manual

[ipv6]
addr-gen-mode=stable-privacy
address1=2a02:2940:0:c007::102/64,2a02:2940:0:c007::1
dns-search=
ip6-privacy=0
method=manual
</file>


===== M/Monit =====

Auf mail/secure läuft M/Monit, um die Statusmeldungen von eigenen und betreuten Servern einzusammeln.

URL holen von ''https://mmonit.com/download/'' (linux-x64)

<code bash>
cd ~
wget https://mmonit.com/dist/mmonit-3.7.3-linux-x64.tar.gz
tar -xvf mmonit-*
sudo mv mmonit-3.7.3 /opt/mmonit
cd /opt/mmonit/
./bin/mmonit
sudo vim /etc/nginx/sites-available/mail.soscisurvey.de
</code>

<code>
    location /mmonit/ {
        proxy_ignore_client_abort on;
        proxy_pass http://127.0.0.1:8080/;
    }
</code>

<code bash>
sudo nginx -t
sudo systemctl reload nginx
</code>

Point your Browser to the [[https://mail.soscisurvey.de/mmonit/|host where mmonit is installed]], and login as user "admin" with password "swordfish".

Dort unter Admin/Users die Passwörter ändern:

  * ''admin'' für die Verwaltung,
  * ''monit'' für die anderen Server, damit sie Daten senden können.

Außerdem Admin/Alerts/Message Settings -> monit@soscisurvey.de

<code bash>
sudo vim /opt/mmonit/docroot/WEB-INF/web.xml
</code>

Damit ''monit'' nur Daten schicken und keine Infos abrufen kann...

<code conf>
            <role-name>admin</role-name>
            <!-- <role-name>*</role-name> -->
</code>

Der Lizenzcode liegt (ganz unten) in ''/opt/mmonit/conf/server.xml''.

Auto-Start einrichten laut [[https://mmonit.com/wiki/MMonit/Setup#autolaunch|offizieller Anleitung]].

<code bash>
sudo vim /etc/monit/conf.d/monit.conf
</code>

<code>
check process mmonit with pidfile /opt/mmonit/logs/mmonit.pid
   start program = "/opt/mmonit/bin/mmonit"
   stop program = "/opt/mmonit/bin/mmonit stop"
</code>

Auf den Nodes/Hosts noch ergänzen...

<code bash>
sudo vim /etc/monit/conf.d/s2survey.conf
sudo systemctl reload monit
</code>

<code>
set eventqueue basedir /var/monit/ slots 1000
set mmonit https://monit:[PASSWORD]@mail.soscisurvey.de/mmonit/collector
</code>


===== SimpleSAML =====

1. SimpleSAMLphp installieren und konfigurieren (siehe [[:de:internal:shibboleth]])

2. In der SoSci-Konfiguration die SSO-Konfiguration ergänzen

<code php>
// ***** Single Sign On (SSO) *****
// Absolute path to the SimpleSAMLPHP directory
$GLOBALS['oFb']['sso']['simplesaml'] = 'C:\Daten\sosci-dev\simplesamlphp-1.19.0';
// ID configured for the login in (see SimpleSAMLPHP -> config/authsources.php)
$GLOBALS['oFb']['sso']['sp'] = 'SoSciSurvey';
// Information about the Identity Provider (IdP)
$GLOBALS['oFb']['sso']['idp']['setup'] = [
    'label' => 'SSO Setup',
    'saml:idp' => 'https://ADD-YOUR-IDP-ADDRESSE-HERE/idp/shibboleth',
    'handler.file' => 'plugins/SSO/SSOSetup.php',
    'handler.class' => 's2survey\account\SSOSetup'
];
</code>

3. SSO-Modul in SoSci hochladen/installieren

4. Klasse in ''plugins/SSO/'' erstellen


===== Live-Monitoring =====

Split-Screen via ''tmux''.

''<strg>+B'' -> ''"'' für horizontale Teilung
''<strg>+B'' -> ''%'' für vertikale Teilung
''<strg>+B'' -> Pfeiltaste zum Umschalten zwischen den Feldern

<code bash>
sudo apachetop -f /var/www/s2survey/log/n-access.log
elinks  http://localhost/status.primary?full
top
</code>

Außerdem Munin im Blick behalten.


===== Bugfixes =====

>  warning: btree:/var/lib/postfix/smtp_scache is unavailable

<code bash>
sudo systemctl stop postfix
sudo rm -f /var/lib/postfix/smtpd_scache.db
sudo systemctl start postfix
</code>

> warning: btree:/var/lib/postfix/smtp_scache is unavailable. open database /var/lib/postfix/smtp_scache.db: File exists

<code bash>
sudo systemctl stop postfix
sudo rm -f /var/lib/postfix/__db.smtp_scache.db
sudo systemctl start postfix
</code>

... oder was auch immer da an alten Dateien liegt und das System blockiert.


> Temporary failure in name resolution

<code bash>
sudo systemctl restart systemd-resolved.service
</code>

Eventuell muss noch ein ''nameserver 9.9.9.9'' in der /etc/resolv.conf ergänzt werden.


===== Einrichtung Testsystem =====

Für einen Penetrationtest wurde 04/2021 ein Testsystem eingerichtet. Hier die wesentlichen Eckpunkte.

Partnergate richtete einen Lon der VM ein und änderte die IP-Adresse:

  * Partnergate braucht einen sudo-Account
  * Nach dem Klonen wurde das Mount ''/mnt/backup'' entfernt.
  * Es wurde nur die IPv4-Adresse geändert, IPv6 bekam der Server nicht
  * Monit muss möglichst als erstes deaktiviert werden ''sudo systemctl stop monit'', damit der Server nicht ständig neu startet.
  * Als nächstes sollte das Cronjob-Passwort in ''system/config.php'' geändert werden, damit keine Serienmails mehr verschickt werden.

Vom Produktiv-System wird erstmal ein neues Backup gezogen.

Folgende Schritte sind für das Testsystem notwendig:

  * Einrichtung einer neuen Lizenz auf dem Distributions-Server
  * Änderung der Server-Adresse über ''admin/install.php'' (evtl. doppelter Login notwendig) + neue Lizenz eintragen
  * Löschen eines Projekts -- und sicherstellen, dass man auf dem richtigen Server ist
  * Leeren der Tabellen ''s2_activities'', ''s2_log'', ''s2_scheduled'' und ''s2_messages''
  * Löschen aller Projekte und Nutzer, abgesehen von einer Handvoll Testprojekte
    * ''DELETE FROM s2_projects WHERE id NOT IN (1, 2, 102)''
    * ''DELETE FROM s2_users WHERE id NOT IN (1, 2, 3, 55)''
    * Tabellen nochmal auf Mailadressen u.ä. kontrollieren
  * Monit auf dem Testsystem einschränken, Reboot deaktivieren
    * Passwörter (2x) entfernen in ''/etc/monit/conf.d/s2survey.conf''
    * Löschen von ''/etc/monit/conf.d/sosci''
  * Alle Verzeichnisse löschen aus ''/root/.duply''
  * ''sudo crontab -e -uroot'' -> Backup, Reboot und Certbot deaktivieren
  * ''sudo crontab -e -uwww-data'' -> Nächtlichen Cronjob deaktivieren
  * IP-Adresse in ''sites-available/02.customers'' ändern und fremde Domains entfernen. Testing-Domain im Port 80 Teil eintragen, damit die Registrierung via Let's Encrypt funktioniert
  * Neues Zertifikat anlegen (s. oben) via Certbot
  * Benutzerkonto für die Tester anlegen, evt. auch admin-Konto übergeben.
  * Passwörter ändern
    * Unix-Nutzer, vgl. ''/etc/shadow'' -- ''mailer'' auch in SoSci Survey eintragen
    * ''authorized_keys'' für ''sosci'' und ''www-ftp'' tauschen (gegen ein neues Schlüsselpaar) -> PuttyGen
    * MySQL-Nutzer alle Passwörter ändern und neues ''s2survey'' Passwort in SoSci Survey eintragen (direkt in der ''config.php'')
    * Munin: ''sudo htpasswd -c /etc/munin/munin-htpasswd admin''
  * Aus ''sytem/keyring/'' alles bis auf den Lizenzserver löschen, anschließend Server-Einstellungen aufrufen, um einen neuen Schlüssel zu erstellen. Diesen auf dem Distributions-Server eintragen (''keyring'' und ''config.php''). Die ''config.php'' auf dem Testsystem anpassen.
  * Passwort für den SMS-Versand aus der SoSci-Konfiguration löschen.
  * Server-Protkolle löschen
    * Logfiles nginx (danach ''sudo systemctl restart nginx'')
    * sosci Logfiles
    * mail.log, danach ''sudo systemctl restart postfix''
    * syslog  ''truncate -s 0 /var/log/syslog''
  * Certbot-Verzeichnisse löschen unter ''/etc/letsencrypt/archive'', ''/etc/letsencrypt/live'', ''/etc/letsencrypt/renewal'', 

===== Zusätzliche Nutzer anlegen =====

<code bash>
sudo su

scriptUserName=USERNAME
useradd -m ${scriptUserName}
passwd ${scriptUserName}

cd /home/${scriptUserName}
mkdir .ssh && chown ${scriptUserName} .ssh && chmod 700 .ssh
touch .ssh/authorized_keys2
chown ${scriptUserName} .ssh/authorized_keys2 && chmod 600 .ssh/authorized_keys2
vim .ssh/authorized_keys2

# Optional
usermod -a -G sudo ${scriptUserName}
</code>